blob: 79e5f8a7bb5ef6239074a04ce8f0ebc6af561447 [file] [log] [blame]
David Turnerd2b1f351999-12-16 23:11:37 +00001/***************************************************************************/
2/* */
3/* ttsbit.c */
4/* */
5/* TrueType and OpenType embedded bitmap support (body). */
6/* */
Werner Lemberg920d41e2000-06-05 14:32:32 +00007/* Copyright 1996-2000 by */
David Turnerd2b1f351999-12-16 23:11:37 +00008/* David Turner, Robert Wilhelm, and Werner Lemberg. */
9/* */
Werner Lemberg920d41e2000-06-05 14:32:32 +000010/* This file is part of the FreeType project, and may only be used, */
11/* modified, and distributed under the terms of the FreeType project */
David Turnerd2b1f351999-12-16 23:11:37 +000012/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
13/* this file you indicate that you have read the license and */
14/* understand and accept it fully. */
15/* */
David Turnerd2b1f351999-12-16 23:11:37 +000016/***************************************************************************/
17
18
David Turnerefce08d2000-05-11 18:23:52 +000019#include <freetype/internal/ftdebug.h>
20#include <freetype/internal/tterrors.h>
21#include <freetype/tttags.h>
David Turnerd2b1f351999-12-16 23:11:37 +000022
23#include <ttsbit.h>
David Turnerd2b1f351999-12-16 23:11:37 +000024
25
26 /*************************************************************************/
27 /* */
28 /* <Function> */
29 /* blit_sbit */
30 /* */
31 /* <Description> */
32 /* Blits a bitmap from an input stream into a given target. Supports */
33 /* x and y offsets as well as byte padded lines. */
34 /* */
35 /* <Input> */
36 /* target :: The target bitmap/pixmap. */
37 /* */
38 /* source :: The input packed bitmap data. */
39 /* */
40 /* line_bits :: The number of bits per line. */
41 /* */
42 /* byte_padded :: A flag which is true if lines are byte-padded. */
43 /* */
44 /* x_offset :: The horizontal offset. */
45 /* */
46 /* y_offset :: The vertical offset. */
47 /* */
48 /* <Note> */
49 /* IMPORTANT: The x and y offsets are relative to the top corner of */
50 /* the target bitmap (unlike the normal TrueType */
51 /* convention). A positive y offset indicates a downwards */
52 /* direction! */
53 /* */
54 static
55 void blit_sbit( FT_Bitmap* target,
David Turner109fcf62000-05-17 23:35:37 +000056 FT_Byte* source,
David Turnerd2b1f351999-12-16 23:11:37 +000057 FT_Int line_bits,
58 FT_Bool byte_padded,
59 FT_Int x_offset,
60 FT_Int y_offset )
61 {
62 FT_Byte* line_buff;
63 FT_Int line_incr;
64 FT_Int height;
65
66 FT_UShort acc;
67 FT_Byte loaded;
68
69
70 /* first of all, compute starting write position */
71 line_incr = target->pitch;
72 line_buff = target->buffer;
David Turnere49ab252000-05-16 23:44:38 +000073
Werner Lemberg920d41e2000-06-05 14:32:32 +000074 if ( line_incr < 0 )
75 line_buff -= line_incr * ( target->rows - 1 );
David Turnerd2b1f351999-12-16 23:11:37 +000076
Werner Lemberg920d41e2000-06-05 14:32:32 +000077 line_buff += ( x_offset >> 3 ) + y_offset * line_incr;
David Turnerd2b1f351999-12-16 23:11:37 +000078
79 /***********************************************************************/
80 /* */
81 /* We use the extra-classic `accumulator' trick to extract the bits */
82 /* from the source byte stream. */
83 /* */
84 /* Namely, the variable `acc' is a 16-bit accumulator containing the */
85 /* last `loaded' bits from the input stream. The bits are shifted to */
86 /* the upmost position in `acc'. */
87 /* */
88 /***********************************************************************/
89
90 acc = 0; /* clear accumulator */
91 loaded = 0; /* no bits were loaded */
92
93 for ( height = target->rows; height > 0; height-- )
94 {
95 FT_Byte* cur = line_buff; /* current write cursor */
96 FT_Int count = line_bits; /* # of bits to extract per line */
97 FT_Byte shift = x_offset & 7; /* current write shift */
98 FT_Byte space = 8 - shift;
99
100
101 /* first of all, read individual source bytes */
102 if ( count >= 8 )
103 {
104 count -= 8;
105 {
106 do
107 {
108 FT_Byte val;
109
Werner Lemberg920d41e2000-06-05 14:32:32 +0000110
David Turnerd2b1f351999-12-16 23:11:37 +0000111 /* ensure that there are at least 8 bits in the accumulator */
112 if ( loaded < 8 )
113 {
Werner Lemberg920d41e2000-06-05 14:32:32 +0000114 acc |= (FT_UShort)*source++ << ( 8 - loaded );
David Turnerd2b1f351999-12-16 23:11:37 +0000115 loaded += 8;
116 }
117
118 /* now write one byte */
Werner Lemberg920d41e2000-06-05 14:32:32 +0000119 val = (FT_Byte)( acc >> 8 );
120 if ( shift )
David Turnerd2b1f351999-12-16 23:11:37 +0000121 {
122 cur[0] |= val >> shift;
123 cur[1] |= val << space;
124 }
125 else
David Turner109fcf62000-05-17 23:35:37 +0000126 cur[0] |= val;
David Turnerd2b1f351999-12-16 23:11:37 +0000127
128 cur++;
129 acc <<= 8; /* remove bits from accumulator */
130 loaded -= 8;
131 count -= 8;
132 }
133 while ( count >= 0 );
134 }
135
136 /* restore `count' to correct value */
137 count += 8;
138 }
139
140 /* now write remaining bits (count < 8) */
141 if ( count > 0 )
142 {
143 FT_Byte val;
144
145
146 /* ensure that there are at least `count' bits in the accumulator */
147 if ( loaded < count )
148 {
Werner Lemberg920d41e2000-06-05 14:32:32 +0000149 acc |= (FT_UShort)*source++ << ( 8 - loaded );
David Turnerd2b1f351999-12-16 23:11:37 +0000150 loaded += 8;
151 }
152
153 /* now write remaining bits */
Werner Lemberg920d41e2000-06-05 14:32:32 +0000154 val = ( (FT_Byte)( acc >> 8 ) ) & ~( 0xFF >> count );
David Turnerd2b1f351999-12-16 23:11:37 +0000155 cur[0] |= val >> shift;
156
157 if ( count > space )
158 cur[1] |= val << space;
159
160 acc <<= count;
161 loaded -= count;
162 }
163
164 /* now, skip to next line */
165 if ( byte_padded )
166 acc = loaded = 0; /* clear accumulator on byte-padded lines */
167
168 line_buff += line_incr;
169 }
170 }
171
172
173 /*************************************************************************/
174 /* */
175 /* <Function> */
176 /* TT_Load_Small_SBit_Metrics */
177 /* */
178 /* <Description> */
179 /* Loads a small bitmap metrics record. */
180 /* */
181 /* <Input> */
182 /* stream :: The input stream. */
183 /* */
184 /* <Output> */
185 /* metrics :: A small metrics structure. */
186 /* */
187 static
188 void TT_Load_Small_SBit_Metrics( TT_SBit_Small_Metrics* metrics,
189 FT_Stream stream )
190 {
191 metrics->height = GET_Byte();
192 metrics->width = GET_Byte();
193 metrics->bearingX = GET_Char();
194 metrics->bearingY = GET_Char();
195 metrics->advance = GET_Byte();
196 }
197
198
199 /*************************************************************************/
200 /* */
201 /* <Function> */
202 /* TT_Load_SBit_Metrics */
203 /* */
204 /* <Description> */
205 /* Loads a bitmap metrics record. */
206 /* */
207 /* <Input> */
208 /* stream :: The input stream. */
209 /* */
210 /* <Output> */
211 /* metrics :: A metrics structure. */
212 /* */
213 static
214 void TT_Load_SBit_Metrics( TT_SBit_Metrics* metrics,
215 FT_Stream stream )
216 {
217 metrics->height = GET_Byte();
218 metrics->width = GET_Byte();
219
220 metrics->horiBearingX = GET_Char();
221 metrics->horiBearingY = GET_Char();
222 metrics->horiAdvance = GET_Byte();
223
224 metrics->vertBearingX = GET_Char();
225 metrics->vertBearingY = GET_Char();
226 metrics->vertAdvance = GET_Byte();
227 }
228
229
230 /*************************************************************************/
231 /* */
232 /* <Function> */
233 /* TT_Load_SBit_Line_Metrics */
234 /* */
235 /* <Description> */
236 /* Loads a bitmap line metrics record. */
237 /* */
238 /* <Input> */
239 /* stream :: The input stream. */
240 /* */
241 /* <Output> */
242 /* metrics :: A line metrics structure. */
243 /* */
244 static
245 void TT_Load_SBit_Line_Metrics( TT_SBit_Line_Metrics* metrics,
246 FT_Stream stream )
247 {
248 metrics->ascender = GET_Char();
249 metrics->descender = GET_Char();
250 metrics->max_width = GET_Byte();
251
252 metrics->caret_slope_numerator = GET_Char();
253 metrics->caret_slope_denominator = GET_Char();
254 metrics->caret_offset = GET_Char();
255
256 metrics->min_origin_SB = GET_Char();
257 metrics->min_advance_SB = GET_Char();
258 metrics->max_before_BL = GET_Char();
259 metrics->min_after_BL = GET_Char();
260 metrics->pads[0] = GET_Char();
261 metrics->pads[1] = GET_Char();
262 }
263
264
265 /*************************************************************************/
266 /* */
267 /* <Function> */
268 /* TT_Load_SBit_Const_Metrics */
269 /* */
270 /* <Description> */
271 /* Loads the metrics for `EBLC' index tables format 2 and 5. */
272 /* */
273 /* <Input> */
274 /* range :: The target range. */
275 /* */
276 /* stream :: The input stream. */
277 /* */
278 /* <Return> */
279 /* TrueType error code. 0 means success. */
280 /* */
281 static
282 TT_Error Load_SBit_Const_Metrics( TT_SBit_Range* range,
283 FT_Stream stream )
284 {
285 TT_Error error;
286
Werner Lemberg920d41e2000-06-05 14:32:32 +0000287
David Turnerd2b1f351999-12-16 23:11:37 +0000288 if ( !ACCESS_Frame( 12L ) )
289 {
290 range->image_size = GET_ULong();
291 TT_Load_SBit_Metrics( &range->metrics, stream );
292
293 FORGET_Frame();
294 }
295
296 return error;
297 }
298
299
300 /*************************************************************************/
301 /* */
302 /* <Function> */
303 /* TT_Load_SBit_Range_Codes */
304 /* */
305 /* <Description> */
306 /* Loads the range codes for `EBLC' index tables format 4 and 5. */
307 /* */
308 /* <Input> */
309 /* range :: The target range. */
310 /* */
311 /* stream :: The input stream. */
312 /* */
313 /* load_offsets :: A flag whether to load the glyph offset table. */
314 /* */
315 /* <Return> */
316 /* TrueType error code. 0 means success. */
317 /* */
318 static
319 TT_Error Load_SBit_Range_Codes( TT_SBit_Range* range,
320 FT_Stream stream,
321 TT_Bool load_offsets )
322 {
323 TT_Error error;
324 TT_ULong count, n, size;
325 FT_Memory memory = stream->memory;
326
327
328 if ( READ_ULong( count ) )
329 goto Exit;
330
331 range->num_glyphs = count;
332
333 /* Allocate glyph offsets table if needed */
334 if ( load_offsets )
335 {
336 if ( ALLOC_ARRAY( range->glyph_offsets, count, TT_ULong ) )
337 goto Exit;
338
339 size = count * 4L;
340 }
341 else
342 size = count * 2L;
343
344 /* Allocate glyph codes table and access frame */
345 if ( ALLOC_ARRAY ( range->glyph_codes, count, TT_UShort ) ||
346 ACCESS_Frame( size ) )
347 goto Exit;
348
349 for ( n = 0; n < count; n++ )
350 {
351 range->glyph_codes[n] = GET_UShort();
352
Werner Lemberg920d41e2000-06-05 14:32:32 +0000353 if ( load_offsets )
David Turnere49ab252000-05-16 23:44:38 +0000354 range->glyph_offsets[n] = (TT_ULong)range->image_offset +
David Turnerd2b1f351999-12-16 23:11:37 +0000355 GET_UShort();
356 }
357
358 FORGET_Frame();
359
360 Exit:
361 return error;
362 }
363
364
365 /*************************************************************************/
366 /* */
367 /* <Function> */
368 /* TT_Load_SBit_Range */
369 /* */
370 /* <Description> */
371 /* Loads a given `EBLC' index/range table. */
372 /* */
373 /* <Input> */
374 /* range :: The target range. */
Werner Lemberg920d41e2000-06-05 14:32:32 +0000375 /* */
David Turnerd2b1f351999-12-16 23:11:37 +0000376 /* stream :: The input stream. */
377 /* */
378 /* <Return> */
379 /* TrueType error code. 0 means success. */
380 /* */
381 static
382 TT_Error Load_SBit_Range( TT_SBit_Range* range,
383 FT_Stream stream )
384 {
385 TT_Error error;
386 FT_Memory memory = stream->memory;
387
388
389 switch( range->index_format )
390 {
391 case 1: /* variable metrics with 4-byte offsets */
392 case 3: /* variable metrics with 2-byte offsets */
393 {
394 TT_ULong num_glyphs, n;
395 TT_Int size_elem;
Werner Lemberg920d41e2000-06-05 14:32:32 +0000396 TT_Bool large = ( range->index_format == 1 );
David Turnerd2b1f351999-12-16 23:11:37 +0000397
398
399 num_glyphs = range->last_glyph - range->first_glyph + 1L;
400 range->num_glyphs = num_glyphs;
Werner Lemberg920d41e2000-06-05 14:32:32 +0000401 num_glyphs++; /* XXX: BEWARE - see spec */
David Turnerd2b1f351999-12-16 23:11:37 +0000402
Werner Lemberg920d41e2000-06-05 14:32:32 +0000403 size_elem = large ? 4 : 2;
David Turnerd2b1f351999-12-16 23:11:37 +0000404
405 if ( ALLOC_ARRAY( range->glyph_offsets,
406 num_glyphs, TT_ULong ) ||
David Turnerd2b1f351999-12-16 23:11:37 +0000407 ACCESS_Frame( num_glyphs * size_elem ) )
408 goto Exit;
409
410 for ( n = 0; n < num_glyphs; n++ )
411 range->glyph_offsets[n] = (TT_ULong)( range->image_offset +
Werner Lemberg920d41e2000-06-05 14:32:32 +0000412 large ? GET_ULong() : GET_UShort() );
David Turnerd2b1f351999-12-16 23:11:37 +0000413 FORGET_Frame();
414 }
415 break;
416
417 case 2: /* all glyphs have identical metrics */
418 error = Load_SBit_Const_Metrics( range, stream );
419 break;
420
421 case 4:
422 error = Load_SBit_Range_Codes( range, stream, 1 );
423 break;
424
425 case 5:
426 error = Load_SBit_Const_Metrics( range, stream ) ||
Werner Lemberg920d41e2000-06-05 14:32:32 +0000427 Load_SBit_Range_Codes( range, stream, 0 );
David Turnerd2b1f351999-12-16 23:11:37 +0000428 break;
429
430 default:
431 error = TT_Err_Invalid_File_Format;
432 }
433
434 Exit:
435 return error;
436 }
437
438
439 /*************************************************************************/
440 /* */
441 /* <Function> */
442 /* TT_Load_SBit_Strikes */
443 /* */
444 /* <Description> */
445 /* Loads the table of embedded bitmap sizes for this face. */
446 /* */
447 /* <Input> */
448 /* face :: The target face object. */
Werner Lemberg920d41e2000-06-05 14:32:32 +0000449 /* */
David Turnerd2b1f351999-12-16 23:11:37 +0000450 /* stream :: The input stream. */
451 /* */
452 /* <Return> */
453 /* TrueType error code. 0 means success. */
454 /* */
455 LOCAL_FUNC
456 TT_Error TT_Load_SBit_Strikes( TT_Face face,
457 FT_Stream stream )
458 {
459 TT_Error error = 0;
460 FT_Memory memory = stream->memory;
461 TT_Fixed version;
462 TT_ULong num_strikes;
463 TT_ULong table_base;
464
Werner Lemberg920d41e2000-06-05 14:32:32 +0000465
David Turner109fcf62000-05-17 23:35:37 +0000466 face->num_sbit_strikes = 0;
David Turnerd2b1f351999-12-16 23:11:37 +0000467
468 /* this table is optional */
469 error = face->goto_table( face, TTAG_EBLC, stream, 0 );
Werner Lemberg920d41e2000-06-05 14:32:32 +0000470 if ( error )
David Turnerd2b1f351999-12-16 23:11:37 +0000471 {
472 error = 0;
473 goto Exit;
474 }
475
476 table_base = FILE_Pos();
477 if ( ACCESS_Frame( 8L ) )
478 goto Exit;
479
480 version = GET_Long();
481 num_strikes = GET_ULong();
482
483 FORGET_Frame();
484
485 /* check version number and strike count */
486 if ( version != 0x00020000 ||
487 num_strikes >= 0x10000 )
488 {
489 FT_ERROR(( "TT_Load_SBit_Strikes: invalid table version!\n" ));
490 error = TT_Err_Invalid_File_Format;
491
492 goto Exit;
493 }
494
495 /* allocate the strikes table */
496 if ( ALLOC_ARRAY( face->sbit_strikes, num_strikes, TT_SBit_Strike ) )
497 goto Exit;
498
499 face->num_sbit_strikes = num_strikes;
500
501 /* now read each strike table separately */
502 {
503 TT_SBit_Strike* strike = face->sbit_strikes;
504 TT_ULong count = num_strikes;
505
Werner Lemberg920d41e2000-06-05 14:32:32 +0000506
David Turnerd2b1f351999-12-16 23:11:37 +0000507 if ( ACCESS_Frame( 48L * num_strikes ) )
508 goto Exit;
509
510 while ( count > 0 )
511 {
512 TT_ULong indexTablesSize;
513
514
515 strike->ranges_offset = GET_ULong();
516 indexTablesSize = GET_ULong(); /* don't save */
517
518 strike->num_ranges = GET_ULong();
519 strike->color_ref = GET_ULong();
520
521 TT_Load_SBit_Line_Metrics( &strike->hori, stream );
522 TT_Load_SBit_Line_Metrics( &strike->vert, stream );
523
524 strike->start_glyph = GET_UShort();
525 strike->end_glyph = GET_UShort();
526 strike->x_ppem = GET_Byte();
527 strike->y_ppem = GET_Byte();
528 strike->bit_depth = GET_Byte();
529 strike->flags = GET_Char();
530
531 count--;
532 strike++;
533 }
534
535 FORGET_Frame();
536 }
537
538 /* allocate the index ranges for each strike table */
539 {
540 TT_SBit_Strike* strike = face->sbit_strikes;
541 TT_ULong count = num_strikes;
542
543
544 while ( count > 0 )
545 {
Werner Lemberg920d41e2000-06-05 14:32:32 +0000546 TT_SBit_Range* range;
547 TT_ULong count2 = strike->num_ranges;
David Turnerd2b1f351999-12-16 23:11:37 +0000548
549
550 if ( ALLOC_ARRAY( strike->sbit_ranges,
551 strike->num_ranges,
552 TT_SBit_Range ) )
553 goto Exit;
554
555 /* read each range */
556 if ( FILE_Seek( table_base + strike->ranges_offset ) ||
557 ACCESS_Frame( strike->num_ranges * 8L ) )
558 goto Exit;
559
560 range = strike->sbit_ranges;
561 while ( count2 > 0 )
562 {
563 range->first_glyph = GET_UShort();
564 range->last_glyph = GET_UShort();
565 range->table_offset = table_base + strike->ranges_offset
566 + GET_ULong();
567 count2--;
568 range++;
569 }
570
571 FORGET_Frame();
572
573 /* Now, read each index table */
574 count2 = strike->num_ranges;
575 range = strike->sbit_ranges;
576 while ( count2 > 0 )
577 {
578 /* Read the header */
579 if ( FILE_Seek( range->table_offset ) ||
580 ACCESS_Frame( 8L ) )
581 goto Exit;
582
583 range->index_format = GET_UShort();
584 range->image_format = GET_UShort();
585 range->image_offset = GET_ULong();
586
587 FORGET_Frame();
588
589 error = Load_SBit_Range( range, stream );
590 if ( error )
591 goto Exit;
592
593 count2--;
594 range++;
595 }
596
597 count--;
598 strike++;
599 }
600 }
601
602 Exit:
603 return error;
604 }
605
606
607 /*************************************************************************/
608 /* */
609 /* <Function> */
610 /* TT_Free_SBit_Strikes */
611 /* */
612 /* <Description> */
613 /* Releases the embedded bitmap tables. */
614 /* */
615 /* <Input> */
616 /* face :: The target face object. */
617 /* */
618 LOCAL_FUNC
619 void TT_Free_SBit_Strikes( TT_Face face )
620 {
621 FT_Memory memory = face->root.memory;
622 TT_SBit_Strike* strike = face->sbit_strikes;
623 TT_SBit_Strike* strike_limit = strike + face->num_sbit_strikes;
624
625
626 if ( strike )
627 {
628 for ( ; strike < strike_limit; strike++ )
629 {
630 TT_SBit_Range* range = strike->sbit_ranges;
631 TT_SBit_Range* range_limit = range + strike->num_ranges;
632
Werner Lemberg920d41e2000-06-05 14:32:32 +0000633
David Turnerd2b1f351999-12-16 23:11:37 +0000634 if ( range )
635 {
636 for ( ; range < range_limit; range++ )
637 {
638 /* release the glyph offsets and codes tables */
639 /* where appropriate */
640 FREE( range->glyph_offsets );
641 FREE( range->glyph_codes );
642 }
643 }
644 FREE( strike->sbit_ranges );
645 strike->num_ranges = 0;
646 }
647 FREE( face->sbit_strikes );
648 }
649 face->num_sbit_strikes = 0;
650 }
651
652
653 /*************************************************************************/
654 /* */
655 /* <Function> */
656 /* Find_SBit_Range */
657 /* */
658 /* <Description> */
659 /* Scans a given strike's ranges and return, for a given glyph */
660 /* index, the corresponding sbit range, and `EBDT' offset. */
661 /* */
662 /* <Input> */
663 /* glyph_index :: The glyph index. */
664 /* strike :: The source/current sbit strike. */
665 /* */
666 /* <Output> */
667 /* arange :: The sbit range containing the glyph index. */
668 /* aglyph_offset :: The offset of the glyph data in `EBDT' table. */
669 /* */
670 /* <Return> */
671 /* TrueType error code. 0 means the glyph index was found. */
672 /* */
673 static
674 TT_Error Find_SBit_Range( TT_UInt glyph_index,
675 TT_SBit_Strike* strike,
676 TT_SBit_Range** arange,
677 TT_ULong* aglyph_offset )
678 {
679 TT_SBit_Range *range, *range_limit;
680
681
682 /* check whether the glyph index is within this strike's */
683 /* glyph range */
684 if ( glyph_index < strike->start_glyph ||
685 glyph_index > strike->end_glyph )
686 goto Fail;
687
688 /* scan all ranges in strike */
689 range = strike->sbit_ranges;
690 range_limit = range + strike->num_ranges;
691 if ( !range )
692 goto Fail;
693
694 for ( ; range < range_limit; range++ )
695 {
696 if ( glyph_index >= range->first_glyph &&
697 glyph_index <= range->last_glyph )
698 {
699 TT_UShort delta = glyph_index - range->first_glyph;
700
701
702 switch ( range->index_format )
703 {
704 case 1:
705 case 3:
706 *aglyph_offset = range->glyph_offsets[delta];
707 break;
708
709 case 2:
710 *aglyph_offset = range->image_offset +
711 range->image_size * delta;
712 break;
713
714 case 4:
715 case 5:
716 {
717 TT_ULong n;
718
719
720 for ( n = 0; n < range->num_glyphs; n++ )
721 {
722 if ( range->glyph_codes[n] == glyph_index )
723 {
724 if ( range->index_format == 4 )
725 *aglyph_offset = range->glyph_offsets[n];
726 else
727 *aglyph_offset = range->image_offset +
728 n * range->image_size;
David Turner109fcf62000-05-17 23:35:37 +0000729 goto Found;
David Turnerd2b1f351999-12-16 23:11:37 +0000730 }
731 }
732 }
733
734 /* fall-through */
735 default:
736 goto Fail;
737 }
738
David Turner109fcf62000-05-17 23:35:37 +0000739 Found:
David Turnerd2b1f351999-12-16 23:11:37 +0000740 /* return successfully! */
741 *arange = range;
David Turnerd2b1f351999-12-16 23:11:37 +0000742 return 0;
743 }
744 }
745
746 Fail:
747 *arange = 0;
748 *aglyph_offset = 0;
749
750 return TT_Err_Invalid_Argument;
751 }
752
753
754 /*************************************************************************/
755 /* */
756 /* <Function> */
757 /* Find_SBit_Image */
758 /* */
759 /* <Description> */
760 /* Checks whether an embedded bitmap (an `sbit') exists for a given */
761 /* glyph, at given x and y ppems. */
762 /* */
763 /* <Input> */
764 /* face :: The target face object. */
765 /* glyph_index :: The glyph index. */
766 /* x_ppem :: The horizontal resolution in points per EM. */
767 /* y_ppem :: The vertical resolution in points per EM. */
768 /* */
769 /* <Output> */
770 /* arange :: The SBit range containing the glyph index. */
771 /* astrike :: The SBit strike containing the glyph index. */
772 /* aglyph_offset :: The offset of the glyph data in `EBDT' table. */
773 /* */
774 /* <Return> */
775 /* TrueType error code. 0 means success. Returns */
Werner Lemberg920d41e2000-06-05 14:32:32 +0000776 /* TT_Err_Invalid_Argument if no sbit exists for the requested glyph. */
David Turnerd2b1f351999-12-16 23:11:37 +0000777 /* */
778 static
779 TT_Error Find_SBit_Image( TT_Face face,
780 TT_UInt glyph_index,
781 TT_Int x_ppem,
782 TT_Int y_ppem,
783
784 TT_SBit_Range** arange,
785 TT_SBit_Strike** astrike,
786 TT_ULong* aglyph_offset )
787 {
788 TT_SBit_Strike* strike = face->sbit_strikes;
789 TT_SBit_Strike* strike_limit = strike + face->num_sbit_strikes;
790
791
Werner Lemberg920d41e2000-06-05 14:32:32 +0000792 if ( !strike )
David Turnerd2b1f351999-12-16 23:11:37 +0000793 goto Fail;
794
795 for ( ; strike < strike_limit; strike++ )
796 {
797 if ( strike->x_ppem == x_ppem && strike->y_ppem == y_ppem )
798 {
799 TT_Error error;
800
801
802 error = Find_SBit_Range( glyph_index, strike, arange, aglyph_offset );
803 if ( error )
804 goto Fail;
805
806 *astrike = strike;
807
808 return TT_Err_Ok;
809 }
810 }
811
812 Fail:
813 /* no embedded bitmap for this glyph in face */
814 *arange = 0;
815 *astrike = 0;
816 *aglyph_offset = 0;
817
818 return TT_Err_Invalid_Argument;
819 }
820
821
822 /*************************************************************************/
823 /* */
824 /* <Function> */
825 /* Load_SBit_Metrics */
826 /* */
827 /* <Description> */
828 /* Gets the big metrics for a given SBit. */
829 /* */
830 /* <Input> */
831 /* stream :: The input stream. */
Werner Lemberg920d41e2000-06-05 14:32:32 +0000832 /* */
David Turnerd2b1f351999-12-16 23:11:37 +0000833 /* range :: The SBit range containing the glyph. */
834 /* */
835 /* <Output> */
836 /* big_metrics :: A big SBit metrics structure for the glyph. */
837 /* */
838 /* <Return> */
839 /* TrueType error code. 0 means success. */
840 /* */
841 /* <Note> */
842 /* The stream cursor must be positioned at the glyph's offset within */
843 /* the `EBDT' table before the call. */
844 /* */
845 /* If the image format uses variable metrics, the stream cursor is */
846 /* positioned just after the metrics header in the `EBDT' table on */
847 /* function exit. */
848 /* */
849 static
850 TT_Error Load_SBit_Metrics( FT_Stream stream,
851 TT_SBit_Range* range,
852 TT_SBit_Metrics* metrics )
853 {
854 TT_Error error = TT_Err_Ok;
855
856
David Turner109fcf62000-05-17 23:35:37 +0000857 switch ( range->image_format )
David Turnerd2b1f351999-12-16 23:11:37 +0000858 {
David Turner109fcf62000-05-17 23:35:37 +0000859 case 1:
860 case 2:
861 case 8:
862 /* variable small metrics */
Werner Lemberg920d41e2000-06-05 14:32:32 +0000863 {
864 TT_SBit_Small_Metrics smetrics;
David Turnerd2b1f351999-12-16 23:11:37 +0000865
David Turner109fcf62000-05-17 23:35:37 +0000866
Werner Lemberg920d41e2000-06-05 14:32:32 +0000867 /* read small metrics */
868 if ( ACCESS_Frame( 5L ) )
869 goto Exit;
870 TT_Load_Small_SBit_Metrics( &smetrics, stream );
871 FORGET_Frame();
David Turner109fcf62000-05-17 23:35:37 +0000872
Werner Lemberg920d41e2000-06-05 14:32:32 +0000873 /* convert it to a big metrics */
874 metrics->height = smetrics.height;
875 metrics->width = smetrics.width;
876 metrics->horiBearingX = smetrics.bearingX;
877 metrics->horiBearingY = smetrics.bearingY;
878 metrics->horiAdvance = smetrics.advance;
879
880 /* these metrics are made up at a higher level when */
881 /* needed. */
882 metrics->vertBearingX = 0;
883 metrics->vertBearingY = 0;
884 metrics->vertAdvance = 0;
885 }
886 break;
David Turner109fcf62000-05-17 23:35:37 +0000887
888 case 6:
889 case 7:
890 case 9:
891 /* variable big metrics */
892 {
893 if ( ACCESS_Frame( 8L ) )
894 goto Exit;
895 TT_Load_SBit_Metrics( metrics, stream );
896 FORGET_Frame();
David Turnerd2b1f351999-12-16 23:11:37 +0000897 }
898 break;
899
David Turner109fcf62000-05-17 23:35:37 +0000900 case 5:
Werner Lemberg920d41e2000-06-05 14:32:32 +0000901 default: /* constant metrics */
David Turner109fcf62000-05-17 23:35:37 +0000902 if ( range->index_format == 2 || range->index_format == 5 )
903 *metrics = range->metrics;
904 else
905 return FT_Err_Invalid_File_Format;
906 }
David Turnerd2b1f351999-12-16 23:11:37 +0000907
908 Exit:
909 return error;
910 }
911
912
913
914 /*************************************************************************/
915 /* */
916 /* <Function> */
917 /* Crop_Bitmap */
918 /* */
919 /* <Description> */
920 /* Crops a bitmap to its tightest bounding box, and adjusts its */
921 /* metrics. */
922 /* */
923 /* <Input> */
924 /* image :: The input glyph slot. */
925 /* */
926 /* metrics :: The corresponding metrics structure. */
927 /* */
928 static
929 void Crop_Bitmap( FT_Bitmap* map,
930 TT_SBit_Metrics* metrics )
931 {
932 /***********************************************************************/
933 /* */
934 /* In this situation, some bounding boxes of embedded bitmaps are too */
935 /* large. We need to crop it to a reasonable size. */
936 /* */
937 /* --------- */
938 /* | | ----- */
939 /* | *** | |***| */
940 /* | * | | * | */
941 /* | * | ------> | * | */
942 /* | * | | * | */
943 /* | * | | * | */
944 /* | *** | |***| */
945 /* --------- ----- */
946 /* */
947 /***********************************************************************/
948
949 TT_Int rows, count;
950 TT_Long line_len;
951 TT_Byte* line;
952
953
954 /***********************************************************************/
955 /* */
956 /* first of all, checks the top-most lines of the bitmap, and removes */
957 /* them if they're empty. */
958 /* */
959 {
960 line = (TT_Byte*)map->buffer;
961 rows = map->rows;
962 line_len = map->pitch;
963
964
965 for ( count = 0; count < rows; count++ )
966 {
967 TT_Byte* cur = line;
968 TT_Byte* limit = line + line_len;
969
970
971 for ( ; cur < limit; cur++ )
972 if ( cur[0] )
973 goto Found_Top;
974
975 /* the current line was empty - skip to next one */
976 line = limit;
977 }
978
979 Found_Top:
980 /* check that we have at least one filled line */
981 if ( count >= rows )
982 goto Empty_Bitmap;
983
984 /* now, crop the empty upper lines */
985 if ( count > 0 )
986 {
987 line = (TT_Byte*)map->buffer;
988
989 MEM_Move( line, line + count * line_len, (rows - count) * line_len );
990
991 metrics->height -= count;
992 metrics->horiBearingY -= count;
993 metrics->vertBearingY -= count;
994
995 map->rows -= count;
996 rows -= count;
997 }
998 }
999
1000 /***********************************************************************/
1001 /* */
1002 /* second, crop the lower lines */
1003 /* */
1004 {
1005 line = (TT_Byte*)map->buffer + (rows - 1) * line_len;
1006
1007 for ( count = 0; count < rows; count++ )
1008 {
1009 TT_Byte* cur = line;
1010 TT_Byte* limit = line + line_len;
1011
1012
1013 for ( ; cur < limit; cur++ )
1014 if ( cur[0] )
1015 goto Found_Bottom;
1016
1017 /* the current line was empty - skip to previous one */
1018 line -= line_len;
1019 }
1020
1021 Found_Bottom:
1022 if ( count > 0 )
1023 {
1024 metrics->height -= count;
1025 rows -= count;
1026 map->rows -= count;
1027 }
1028 }
1029
1030 /***********************************************************************/
1031 /* */
1032 /* third, get rid of the space on the left side of the glyph */
1033 /* */
1034 do
1035 {
1036 TT_Byte* limit;
1037
1038
1039 line = (TT_Byte*)map->buffer;
1040 limit = line + rows * line_len;
1041
1042 for ( ; line < limit; line += line_len )
1043 if ( line[0] & 0x80 )
1044 goto Found_Left;
1045
1046 /* shift the whole glyph one pixel to the left */
1047 line = (TT_Byte*)map->buffer;
1048 limit = line + rows * line_len;
1049
1050 for ( ; line < limit; line += line_len )
1051 {
1052 TT_Int n, width = map->width;
1053 TT_Byte old;
1054 TT_Byte* cur = line;
1055
1056
1057 old = cur[0] << 1;
1058 for ( n = 8; n < width; n += 8 )
1059 {
1060 TT_Byte val;
1061
1062
1063 val = cur[1];
Werner Lemberg920d41e2000-06-05 14:32:32 +00001064 cur[0] = old | ( val >> 7 );
David Turnerd2b1f351999-12-16 23:11:37 +00001065 old = val << 1;
1066 cur++;
1067 }
1068 cur[0] = old;
1069 }
1070
1071 map->width--;
1072 metrics->horiBearingX++;
1073 metrics->vertBearingX++;
1074 metrics->width--;
1075 } while ( map->width > 0 );
1076
1077 Found_Left:
1078
1079 /***********************************************************************/
1080 /* */
1081 /* finally, crop the bitmap width to get rid of the space on the right */
1082 /* side of the glyph. */
1083 /* */
1084 do
1085 {
Werner Lemberg920d41e2000-06-05 14:32:32 +00001086 TT_Int right = map->width - 1;
David Turnerd2b1f351999-12-16 23:11:37 +00001087 TT_Byte* limit;
1088 TT_Byte mask;
1089
1090
Werner Lemberg920d41e2000-06-05 14:32:32 +00001091 line = (TT_Byte*)map->buffer + ( right >> 3 );
1092 limit = line + rows * line_len;
1093 mask = 0x80 >> ( right & 7 );
David Turnerd2b1f351999-12-16 23:11:37 +00001094
1095 for ( ; line < limit; line += line_len )
1096 if ( line[0] & mask )
1097 goto Found_Right;
1098
1099 /* crop the whole glyph to the right */
1100 map->width--;
1101 metrics->width--;
1102 } while ( map->width > 0 );
1103
1104 Found_Right:
1105 /* all right, the bitmap was cropped */
1106 return;
1107
1108 Empty_Bitmap:
1109 map->width = 0;
1110 map->rows = 0;
1111 map->pitch = 0;
1112 map->pixel_mode = ft_pixel_mode_mono;
1113 }
1114
1115
1116 static
1117 TT_Error Load_SBit_Single( FT_Bitmap* map,
1118 TT_Int x_offset,
1119 TT_Int y_offset,
1120 TT_Int pix_bits,
1121 TT_UShort image_format,
1122 TT_SBit_Metrics* metrics,
1123 FT_Stream stream )
1124 {
1125 TT_Error error;
1126
1127
1128 /* check that the source bitmap fits into the target pixmap */
1129 if ( x_offset < 0 || x_offset + metrics->width > map->width ||
1130 y_offset < 0 || y_offset + metrics->height > map->rows )
1131 {
1132 error = TT_Err_Invalid_Argument;
1133
1134 goto Exit;
1135 }
1136
1137 {
1138 TT_Int glyph_width = metrics->width;
1139 TT_Int glyph_height = metrics->height;
1140 TT_Int glyph_size;
1141 TT_Int line_bits = pix_bits * glyph_width;
1142 TT_Bool pad_bytes = 0;
1143
1144
1145 /* compute size of glyph image */
1146 switch ( image_format )
1147 {
1148 case 1: /* byte-padded formats */
1149 case 6:
1150 {
1151 TT_Int line_length;
1152
1153
1154 switch ( pix_bits )
1155 {
Werner Lemberg920d41e2000-06-05 14:32:32 +00001156 case 1: line_length = ( glyph_width + 7 ) >> 3; break;
1157 case 2: line_length = ( glyph_width + 3 ) >> 2; break;
1158 case 4: line_length = ( glyph_width + 1 ) >> 1; break;
1159 default: line_length = glyph_width;
David Turnerd2b1f351999-12-16 23:11:37 +00001160 }
1161
1162 glyph_size = glyph_height * line_length;
1163 pad_bytes = 1;
1164 }
1165 break;
1166
1167 case 2:
1168 case 5:
1169 case 7:
1170 line_bits = glyph_width * pix_bits;
1171 glyph_size = (glyph_height * line_bits + 7) >> 3;
1172 break;
1173
1174 default: /* invalid format */
1175 return TT_Err_Invalid_File_Format;
1176 }
1177
1178 /* Now read data and draw glyph into target pixmap */
1179 if ( ACCESS_Frame( glyph_size ) )
1180 goto Exit;
1181
1182 /* don't forget to multiply `x_offset' by `map->pix_bits' as */
1183 /* the sbit blitter doesn't make a difference between pixmap */
1184 /* depths. */
David Turner109fcf62000-05-17 23:35:37 +00001185 blit_sbit( map, (FT_Byte*)stream->cursor, line_bits, pad_bytes,
David Turnerd2b1f351999-12-16 23:11:37 +00001186 x_offset * pix_bits, y_offset );
1187
1188 FORGET_Frame();
1189 }
1190
1191 Exit:
1192 return error;
1193 }
1194
1195
1196 static
1197 TT_Error Load_SBit_Image( TT_SBit_Strike* strike,
1198 TT_SBit_Range* range,
1199 TT_ULong ebdt_pos,
1200 TT_ULong glyph_offset,
1201 FT_Bitmap* map,
1202 TT_Int x_offset,
1203 TT_Int y_offset,
1204 FT_Stream stream,
1205 TT_SBit_Metrics* metrics )
1206 {
Werner Lemberg920d41e2000-06-05 14:32:32 +00001207 FT_Memory memory = stream->memory;
1208 TT_Error error;
David Turnerd2b1f351999-12-16 23:11:37 +00001209
1210
1211 /* place stream at beginning of glyph data and read metrics */
1212 if ( FILE_Seek( ebdt_pos + glyph_offset ) )
1213 goto Exit;
1214
1215 error = Load_SBit_Metrics( stream, range, metrics );
1216 if ( error )
1217 goto Exit;
1218
1219 /* this function is recursive. At the top-level call, the */
1220 /* field map.buffer is NULL. We thus begin by finding the */
1221 /* dimensions of the higher-level glyph to allocate the */
1222 /* final pixmap buffer */
1223 if ( map->buffer == 0 )
1224 {
1225 TT_Long size;
1226
1227
1228 map->width = metrics->width;
1229 map->rows = metrics->height;
1230
1231 switch ( strike->bit_depth )
1232 {
1233 case 1:
David Turnere49ab252000-05-16 23:44:38 +00001234 map->pixel_mode = ft_pixel_mode_mono;
Werner Lemberg920d41e2000-06-05 14:32:32 +00001235 map->pitch = ( map->width + 7 ) >> 3;
David Turnerd2b1f351999-12-16 23:11:37 +00001236 break;
1237 case 2:
David Turnere49ab252000-05-16 23:44:38 +00001238 map->pixel_mode = ft_pixel_mode_pal2;
Werner Lemberg920d41e2000-06-05 14:32:32 +00001239 map->pitch = ( map->width + 3 ) >> 2;
David Turnerd2b1f351999-12-16 23:11:37 +00001240 break;
1241 case 4:
David Turnere49ab252000-05-16 23:44:38 +00001242 map->pixel_mode = ft_pixel_mode_pal4;
Werner Lemberg920d41e2000-06-05 14:32:32 +00001243 map->pitch = ( map->width + 1 ) >> 1;
David Turnerd2b1f351999-12-16 23:11:37 +00001244 break;
1245 case 8:
David Turnere49ab252000-05-16 23:44:38 +00001246 map->pixel_mode = ft_pixel_mode_grays;
David Turnerd2b1f351999-12-16 23:11:37 +00001247 map->pitch = map->width;
1248 break;
1249
1250 default:
1251 return TT_Err_Invalid_File_Format;
1252 }
1253
1254 size = map->rows * map->pitch;
1255
1256 /* check that there is no empty image */
1257 if ( size == 0 )
1258 goto Exit; /* exit successfully! */
1259
1260 if ( ALLOC( map->buffer, size ) )
1261 goto Exit;
1262 }
1263
1264 switch ( range->image_format )
1265 {
1266 case 1: /* single sbit image - load it */
1267 case 2:
1268 case 5:
1269 case 6:
1270 case 7:
1271 return Load_SBit_Single( map, x_offset, y_offset, strike->bit_depth,
1272 range->image_format, metrics, stream );
1273
1274 case 8: /* compound format */
David Turner109fcf62000-05-17 23:35:37 +00001275 FT_Skip_Stream( stream, 1L );
1276 /* fallthrough */
1277
David Turnerd2b1f351999-12-16 23:11:37 +00001278 case 9:
1279 break;
1280
1281 default: /* invalid image format */
1282 return TT_Err_Invalid_File_Format;
1283 }
1284
1285 /* All right, we're in a compound format. First of all, read */
1286 /* the array of elements */
1287 {
1288 TT_SBit_Component* components;
1289 TT_SBit_Component* comp;
1290 TT_UShort num_components, count;
1291
1292
1293 if ( READ_UShort( num_components ) ||
1294 ALLOC_ARRAY( components, num_components, TT_SBit_Component ) )
1295 goto Exit;
1296
1297 count = num_components;
1298
1299 if ( ACCESS_Frame( 4L * num_components ) )
1300 goto Fail_Memory;
1301
1302 for ( comp = components; count > 0; count--, comp++ )
1303 {
1304 comp->glyph_code = GET_UShort();
1305 comp->x_offset = GET_Char();
1306 comp->y_offset = GET_Char();
1307 }
1308
1309 FORGET_Frame();
1310
1311 /* Now recursively load each element glyph */
1312 count = num_components;
1313 comp = components;
1314 for ( ; count > 0; count--, comp++ )
1315 {
1316 TT_SBit_Range* elem_range;
1317 TT_SBit_Metrics elem_metrics;
1318 TT_ULong elem_offset;
1319
1320
1321 /* find the range for this element */
1322 error = Find_SBit_Range( comp->glyph_code,
1323 strike,
1324 &elem_range,
1325 &elem_offset );
1326 if ( error )
1327 goto Fail_Memory;
1328
1329 /* now load the element, recursively */
1330 error = Load_SBit_Image( strike,
1331 elem_range,
1332 ebdt_pos,
1333 elem_offset,
1334 map,
1335 x_offset + comp->x_offset,
1336 y_offset + comp->y_offset,
1337 stream,
1338 &elem_metrics );
1339 if ( error )
1340 goto Fail_Memory;
1341 }
1342
1343 Fail_Memory:
1344 FREE( components );
1345 }
1346
1347 Exit:
1348 return error;
1349 }
1350
1351
1352 /*************************************************************************/
1353 /* */
1354 /* <Function> */
1355 /* TT_Load_SBit_Image */
1356 /* */
1357 /* <Description> */
1358 /* Loads a given glyph sbit image from the font resource. This also */
1359 /* returns its metrics. */
1360 /* */
1361 /* <Input> */
1362 /* face :: The target face object. */
1363 /* */
1364 /* x_ppem :: The horizontal resolution in points per EM. */
1365 /* */
1366 /* y_ppem :: The vertical resolution in points per EM. */
1367 /* */
1368 /* glyph_index :: The current glyph index. */
1369 /* */
Werner Lemberg920d41e2000-06-05 14:32:32 +00001370 /* load_flags :: The glyph load flags (the code checks for the flag */
1371 /* FT_LOAD_CROP_BITMAP */
1372 /* */
David Turnerd2b1f351999-12-16 23:11:37 +00001373 /* stream :: The input stream. */
1374 /* */
1375 /* <Output> */
1376 /* map :: The target pixmap. */
Werner Lemberg920d41e2000-06-05 14:32:32 +00001377 /* */
David Turnerd2b1f351999-12-16 23:11:37 +00001378 /* metrics :: A big sbit metrics structure for the glyph image. */
1379 /* */
1380 /* <Return> */
1381 /* TrueType error code. 0 means success. Returns an error if no */
1382 /* glyph sbit exists for the index. */
1383 /* */
1384 /* <Note> */
1385 /* The `map.buffer' field is always freed before the glyph is loaded. */
1386 /* */
1387 LOCAL_FUNC
1388 TT_Error TT_Load_SBit_Image( TT_Face face,
1389 TT_Int x_ppem,
1390 TT_Int y_ppem,
1391 TT_UInt glyph_index,
David Turner109fcf62000-05-17 23:35:37 +00001392 TT_UInt load_flags,
David Turnerd2b1f351999-12-16 23:11:37 +00001393 FT_Stream stream,
1394 FT_Bitmap* map,
1395 TT_SBit_Metrics* metrics )
1396 {
1397 TT_Error error;
1398 FT_Memory memory = stream->memory;
1399 TT_ULong ebdt_pos, glyph_offset;
1400
1401 TT_SBit_Strike* strike;
1402 TT_SBit_Range* range;
1403
1404
1405 /* Check whether there is a glyph sbit for the current index */
1406 error = Find_SBit_Image( face, glyph_index, x_ppem, y_ppem,
1407 &range, &strike, &glyph_offset );
1408 if ( error )
1409 goto Exit;
1410
1411 /* now, find the location of the `EBDT' table in */
1412 /* the font file */
1413 error = face->goto_table( face, TTAG_EBDT, stream, 0 );
Werner Lemberg920d41e2000-06-05 14:32:32 +00001414 if ( error )
1415 goto Exit;
David Turnere49ab252000-05-16 23:44:38 +00001416
David Turnerd2b1f351999-12-16 23:11:37 +00001417 ebdt_pos = FILE_Pos();
1418
1419 /* clear the bitmap & load the bitmap */
Werner Lemberg920d41e2000-06-05 14:32:32 +00001420 if ( face->root.glyph->flags & ft_glyph_own_bitmap )
David Turnerc60c61c2000-05-12 15:26:58 +00001421 FREE( map->buffer );
David Turnere49ab252000-05-16 23:44:38 +00001422
David Turnerd2b1f351999-12-16 23:11:37 +00001423 map->rows = map->pitch = map->width = 0;
1424
1425 error = Load_SBit_Image( strike, range, ebdt_pos, glyph_offset,
1426 map, 0, 0, stream, metrics );
1427 if ( error )
1428 goto Exit;
1429
David Turnerc60c61c2000-05-12 15:26:58 +00001430 /* the glyph slot owns this bitmap buffer */
1431 face->root.glyph->flags |= ft_glyph_own_bitmap;
David Turnere49ab252000-05-16 23:44:38 +00001432
David Turnerd2b1f351999-12-16 23:11:37 +00001433 /* setup vertical metrics if needed */
1434 if ( strike->flags & 1 )
1435 {
1436 /* in case of a horizontal strike only */
1437 FT_Int advance;
1438 FT_Int top;
1439
1440
1441 advance = strike->hori.ascender - strike->hori.descender;
1442 top = advance / 10;
1443
1444 metrics->vertBearingX = -metrics->width / 2;
1445 metrics->vertBearingY = advance / 10;
1446 metrics->vertAdvance = advance * 12 / 10;
1447 }
1448
David Turner109fcf62000-05-17 23:35:37 +00001449 /* Crop the bitmap now, unless specified otherwise */
Werner Lemberg920d41e2000-06-05 14:32:32 +00001450 if ( load_flags & FT_LOAD_CROP_BITMAP )
David Turner109fcf62000-05-17 23:35:37 +00001451 Crop_Bitmap( map, metrics );
David Turnerd2b1f351999-12-16 23:11:37 +00001452
1453 Exit:
1454 return error;
1455 }
1456
1457
1458/* END */