[webkit-changes] cvs commit: WebKit/WebCoreSupport.subproj WebTextRenderer.m

David harrison at opensource.apple.com
Fri Jul 29 16:40:31 PDT 2005


harrison    05/07/29 16:40:30

  Modified:    .        ChangeLog
               WebCoreSupport.subproj WebTextRenderer.m
  Log:
          Reviewed by Dave Hyatt (rendering) and Maciej (editing and performance improvements).
  
          Test cases added: Existing tab-related basic editing tests were updated.  More complex tests are coming soon.
  
          <rdar://problem/3792529> REGRESSION (Mail): Tabs do not work the way they did in Panther (especially useful in plain text mail)
  
          Basic strategy is to put tabs into spans with white-space:pre style, and
          render them with tabs stops every 8th space, where the space width and
          the left margin are those of the enclosing block.
  
          * WebCoreSupport.subproj/WebTextRenderer.m:
          (isSpace):
          (isRoundingHackCharacter):
          (getUncachedWidth):
          (-[WebTextRenderer drawLineForCharacters:yOffset:width:color:thickness:]):
          (-[WebTextRenderer _computeWidthForSpace]):
          (_drawGlyphs):
          (-[WebTextRenderer _CG_drawHighlightForRun:style:geometry:]):
          (-[WebTextRenderer _CG_floatWidthForRun:style:widths:fonts:glyphs:startPosition:numGlyphs:]):
          (-[WebTextRenderer _extendCharacterToGlyphMapToInclude:]):
          (-[WebTextRenderer _CG_pointToOffset:style:position:reversed:includePartialGlyphs:]):
          (glyphForCharacter):
          (initializeCharacterWidthIterator):
          (ceilCurrentWidth):
          (widthForNextCharacter):
  
  Revision  Changes    Path
  1.3267    +28 -0     WebKit/ChangeLog
  
  Index: ChangeLog
  ===================================================================
  RCS file: /cvs/root/WebKit/ChangeLog,v
  retrieving revision 1.3266
  retrieving revision 1.3267
  diff -u -r1.3266 -r1.3267
  --- ChangeLog	29 Jul 2005 21:16:53 -0000	1.3266
  +++ ChangeLog	29 Jul 2005 23:40:22 -0000	1.3267
  @@ -1,3 +1,31 @@
  +2005-07-29  David Harrison  <harrison at apple.com>
  +
  +        Reviewed by Dave Hyatt (rendering) and Maciej (editing and performance improvements).
  +
  +        Test cases added: Existing tab-related basic editing tests were updated.  More complex tests are coming soon.
  +
  +        <rdar://problem/3792529> REGRESSION (Mail): Tabs do not work the way they did in Panther (especially useful in plain text mail)
  +        
  +        Basic strategy is to put tabs into spans with white-space:pre style, and
  +        render them with tabs stops every 8th space, where the space width and
  +        the left margin are those of the enclosing block.
  +
  +        * WebCoreSupport.subproj/WebTextRenderer.m:
  +        (isSpace):
  +        (isRoundingHackCharacter):
  +        (getUncachedWidth):
  +        (-[WebTextRenderer drawLineForCharacters:yOffset:width:color:thickness:]):
  +        (-[WebTextRenderer _computeWidthForSpace]):
  +        (_drawGlyphs):
  +        (-[WebTextRenderer _CG_drawHighlightForRun:style:geometry:]):
  +        (-[WebTextRenderer _CG_floatWidthForRun:style:widths:fonts:glyphs:startPosition:numGlyphs:]):
  +        (-[WebTextRenderer _extendCharacterToGlyphMapToInclude:]):
  +        (-[WebTextRenderer _CG_pointToOffset:style:position:reversed:includePartialGlyphs:]):
  +        (glyphForCharacter):
  +        (initializeCharacterWidthIterator):
  +        (ceilCurrentWidth):
  +        (widthForNextCharacter):
  +
   2005-07-29  John Sullivan  <sullivan at apple.com>
   
           Reviewed by Dave Hyatt.
  
  
  
  1.183     +90 -87    WebKit/WebCoreSupport.subproj/WebTextRenderer.m
  
  Index: WebTextRenderer.m
  ===================================================================
  RCS file: /cvs/root/WebKit/WebCoreSupport.subproj/WebTextRenderer.m,v
  retrieving revision 1.182
  retrieving revision 1.183
  diff -u -r1.182 -r1.183
  --- WebTextRenderer.m	23 Jul 2005 20:45:31 -0000	1.182
  +++ WebTextRenderer.m	29 Jul 2005 23:40:30 -0000	1.183
  @@ -55,10 +55,6 @@
   #define POP_DIRECTIONAL_FORMATTING 0x202C
   #define LEFT_TO_RIGHT_OVERRIDE 0x202D
   
  -// Lose precision beyond 1000ths place. This is to work around an apparent
  -// bug in CoreGraphics where there seem to be small errors to some metrics.
  -#define CEIL_TO_INT(x) ((int)(x + 0.999)) /* ((int)(x + 1.0 - FLT_EPSILON)) */
  -
   // MAX_GLYPH_EXPANSION is the maximum numbers of glyphs that may be
   // use to represent a single Unicode code point.
   #define MAX_GLYPH_EXPANSION 4
  @@ -75,7 +71,7 @@
   
   #define ATSFontRefFromNSFont(font) (FMGetATSFontRefFromFont((FMFont)WKGetNSFontATSUFontId(font)))
   
  -#define SMALLCAPS_FONTSIZE_MULTIPLIER 0.7
  +#define SMALLCAPS_FONTSIZE_MULTIPLIER 0.7F
   #define INVALID_WIDTH -(__FLT_MAX__)
   
   #if !defined(ScaleEmToUnits)
  @@ -132,8 +128,8 @@
       unsigned currentCharacter;
       float runWidthSoFar;
       float widthToStart;
  -    int padding;
  -    int padPerSpace;
  +    float padding;
  +    float padPerSpace;
   };
   
   // Internal API
  @@ -179,11 +175,11 @@
   
   static inline BOOL isSpace(UniChar c)
   {
  -    return c == SPACE || c == '\n' || c == NO_BREAK_SPACE;
  +    return c == SPACE || c == '\t' || c == '\n' || c == NO_BREAK_SPACE;
   }
   
   static const uint8_t isRoundingHackCharacterTable[0x100] = {
  -    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 /*\n*/, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  +    0, 0, 0, 0, 0, 0, 0, 0, 0, 1 /*\t*/, 1 /*\n*/, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
       1 /*space*/, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 /*-*/, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 /*?*/,
       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  @@ -195,7 +191,7 @@
   
   static inline BOOL isRoundingHackCharacter(UniChar c)
   {
  -    return (c & ~0xFF) == 0 && isRoundingHackCharacterTable[c];
  +    return (((c & ~0xFF) == 0 && isRoundingHackCharacterTable[c]));
   }
   
   // Map utility functions
  @@ -220,7 +216,7 @@
   
       if (!CGFontGetGlyphScaledAdvances ([font _backingCGSFont], &glyph, 1, &width, [font pointSize])) {
           ERROR ("Unable to cache glyph widths for %@ %f",  [font displayName], [font pointSize]);
  -	return 0.;
  +	return 0.0F;
       }
   
       return width;
  @@ -577,36 +573,27 @@
       cgContext = (CGContextRef)[graphicsContext graphicsPort];
   
       // hack to make thickness 2 underlines for internation text input look right
  -    if (thickness > 1.5 && thickness < 2.5) {
  -        yOffset += .5;
  +    if (thickness > 1.5F && thickness < 2.5F) {
  +        yOffset += .5F;
       }
   
  -    if (thickness == 0.0) {
  -        CGSize size = CGSizeApplyAffineTransform(CGSizeMake(1.0, 1.0), CGAffineTransformInvert(CGContextGetCTM(cgContext)));
  +    if (thickness == 0.0F) {
  +        CGSize size = CGSizeApplyAffineTransform(CGSizeMake(1.0F, 1.0F), CGAffineTransformInvert(CGContextGetCTM(cgContext)));
           CGContextSetLineWidth(cgContext, size.width);
       } else {
           CGContextSetLineWidth(cgContext, thickness);
       }
   
   
  -    // With Q2DX turned on CGContextStrokeLineSegments sometimes fails to draw lines.  See 3952084.
  -    // So, it has been requested that we turn off use of the new API until 3952084 is fixed.
  -#if 1         
  -//#if BUILDING_ON_PANTHER         
  -    CGContextMoveToPoint(cgContext, point.x, point.y + [self lineSpacing] + 1.5 - [self descent] + yOffset);
  -    // Subtract 1 to ensure that the line is always within bounds of element.
  -    CGContextAddLineToPoint(cgContext, point.x + width - 1.0, point.y + [self lineSpacing] + 1.5 - [self descent] + yOffset);
  -    CGContextStrokePath(cgContext);
  -#else
       // Use CGContextStrokeLineSegments on Tiger.  J. Burkey says this will be a big performance win.
  -
  +    // With Q2DX turned on CGContextStrokeLineSegments sometimes fails to draw lines.  See 3952084.
  +    // Tiger shipped with Q2DX disabled, tho, so we can use CGContextStrokeLineSegments.
       CGPoint linePoints[2];
       linePoints[0].x = point.x;
  -    linePoints[0].y = point.y + [self lineSpacing] + 1.5 - [self descent] + yOffset;
  -    linePoints[1].x = point.x + width - 1.0;
  +    linePoints[0].y = point.y + [self lineSpacing] + 1.5F - [self descent] + yOffset;
  +    linePoints[1].x = point.x + width - 1.0F;
       linePoints[1].y = linePoints[0].y;
       CGContextStrokeLineSegments (cgContext, linePoints, 2);
  -#endif
   
       [graphicsContext setShouldAntialias: flag];
   }
  @@ -858,7 +845,7 @@
       spaceWidth = width;
   
       treatAsFixedPitch = [[WebTextRendererFactory sharedFactory] isFontFixedPitch:font];
  -    adjustedSpaceWidth = treatAsFixedPitch ? CEIL_TO_INT(width) : (int)ROUND_TO_INT(width);
  +    adjustedSpaceWidth = treatAsFixedPitch ? ceilf(width) : (int)ROUND_TO_INT(width);
       
       return YES;
   }
  @@ -979,7 +966,7 @@
           float flip = [v isFlipped] ? -1 : 1;
           CGContextSetTextMatrix(cgContext, CGAffineTransformMake(matrix[0], matrix[1] * flip, matrix[2], matrix[3] * flip, matrix[4], matrix[5]));
   		WKSetCGFontRenderingMode(cgContext, drawFont);
  -        CGContextSetFontSize(cgContext, 1.0);
  +        CGContextSetFontSize(cgContext, 1.0F);
   #endif
   
           [color set];
  @@ -1012,7 +999,7 @@
       }
       float startX = startPosition + geometry->point.x;
       
  -    float backgroundWidth = 0.0;
  +    float backgroundWidth = 0.0F;
       while (widthIterator.currentCharacter < (unsigned)run->to) {
           backgroundWidth += widthForNextCharacter(&widthIterator, 0, 0);
       }
  @@ -1194,7 +1181,7 @@
   
   - (float)_CG_floatWidthForRun:(const WebCoreTextRun *)run style:(const WebCoreTextStyle *)style widths: (float *)widthBuffer fonts: (NSFont **)fontBuffer glyphs: (CGGlyph *)glyphBuffer startPosition:(float *)startPosition numGlyphs: (int *)_numGlyphs
   {
  -    float _totalWidth = 0, _nextWidth;
  +    float _nextWidth;
       CharacterWidthIterator widthIterator;
       NSFont *fontUsed = 0;
       ATSGlyphRef glyphUsed;
  @@ -1203,7 +1190,10 @@
       initializeCharacterWidthIterator(&widthIterator, self, run, style);
       if (startPosition)
           *startPosition = widthIterator.widthToStart;
  -    while ((_nextWidth = widthForNextCharacter(&widthIterator, &glyphUsed, &fontUsed)) != INVALID_WIDTH){
  +    while (widthIterator.currentCharacter < (unsigned)widthIterator.run->to) {
  +        _nextWidth = widthForNextCharacter(&widthIterator, &glyphUsed, &fontUsed);
  +        if (_nextWidth == INVALID_WIDTH)
  +            break;
           if (fontBuffer)
               fontBuffer[numGlyphs] = fontUsed;
           if (glyphBuffer)
  @@ -1211,13 +1201,12 @@
           if (widthBuffer)
               widthBuffer[numGlyphs] = _nextWidth;
           numGlyphs++;
  -        _totalWidth += _nextWidth;
       }
           
       if (_numGlyphs)
           *_numGlyphs = numGlyphs;
   
  -    return _totalWidth;
  +    return widthIterator.runWidthSoFar;
   }
   
   - (ATSGlyphRef)_extendUnicodeCharacterToGlyphMapToInclude:(UnicodeChar)c
  @@ -1339,8 +1328,9 @@
               buffer[i] = ZERO_WIDTH_SPACE;
           buffer[0x7F] = ZERO_WIDTH_SPACE;
   
  -        // But both \n and nonbreaking space must render as a space.
  +        // But \n, \t, and nonbreaking space must render as a space.
           buffer['\n'] = ' ';
  +        buffer['\t'] = ' ';
           buffer[NO_BREAK_SPACE] = ' ';
       }
   
  @@ -1813,7 +1803,7 @@
   - (int)_CG_pointToOffset:(const WebCoreTextRun *)run style:(const WebCoreTextStyle *)style position:(int)x reversed:(BOOL)reversed includePartialGlyphs:(BOOL)includePartialGlyphs
   {
       float delta = (float)x;
  -    float width;
  +    float width;   ///  FIX: CHECK THIS
       unsigned offset = run->from;
       CharacterWidthIterator widthIterator;
       
  @@ -1907,13 +1897,17 @@
   {
       if (map == 0)
           return nonGlyphID;
  -        
  +    
  +    // this loop is hot, so it is written to avoid LSU stalls
       while (map) {
  -        if (c >= map->startRange && c <= map->endRange){
  -            *font = map->glyphs[c-map->startRange].font;
  -            return map->glyphs[c-map->startRange].glyph;
  +        UniChar start = map->startRange;
  +        GlyphMap *nextMap = map->next;
  +        if (c >= start && c <= map->endRange){
  +            GlyphEntry *ge = &map->glyphs[c-start];
  +            *font = ge->font;
  +            return ge->glyph;
           }
  -        map = map->next;
  +        map = nextMap;
       }
       return nonGlyphID;
   }
  @@ -1982,7 +1976,7 @@
                   numSpaces++;
               }
           }
  -        iterator->padPerSpace = CEIL_TO_INT ((((float)style->padding) / ((float)numSpaces)));
  +        iterator->padPerSpace = ceilf((((float)style->padding) / ((float)numSpaces)));
       }
       else {
           iterator->padPerSpace = 0;
  @@ -2009,7 +2003,8 @@
   
   static inline float ceilCurrentWidth (CharacterWidthIterator *iterator)
   {
  -    float delta = CEIL_TO_INT(iterator->widthToStart + iterator->runWidthSoFar) - (iterator->widthToStart + iterator->runWidthSoFar);
  +    float totalWidth = iterator->widthToStart + iterator->runWidthSoFar;
  +    float delta = ceilf(totalWidth) - totalWidth;
       iterator->runWidthSoFar += delta;
       return delta;
   }
  @@ -2026,15 +2021,10 @@
       unsigned currentCharacter = iterator->currentCharacter;
   
       NSFont *_fontUsed = nil;
  -    ATSGlyphRef _glyphUsed;
  -
  -    if (!fontUsed)
  -        fontUsed = &_fontUsed;
  -    if (!glyphUsed)
  -        glyphUsed = &_glyphUsed;
  -        
  +    ATSGlyphRef _glyphUsed = nil;
  +    
  +    // Check for end of run.
       if (currentCharacter >= (unsigned)run->to)
  -        // Error! Offset specified beyond end of run.
           return INVALID_WIDTH;
   
       const UniChar *cp = &run->characters[currentCharacter];
  @@ -2101,14 +2091,14 @@
       }
       
       if (c <= 0xFFFF) {
  -        *glyphUsed = glyphForCharacter(renderer->characterToGlyphMap, c, fontUsed);
  -        if (*glyphUsed == nonGlyphID) {
  -            *glyphUsed = [renderer _extendCharacterToGlyphMapToInclude:c];
  +        _glyphUsed = glyphForCharacter(renderer->characterToGlyphMap, c, &_fontUsed);
  +        if (_glyphUsed == nonGlyphID) {
  +            _glyphUsed = [renderer _extendCharacterToGlyphMapToInclude:c];
           }
       } else {
  -        *glyphUsed = glyphForUnicodeCharacter(renderer->unicodeCharacterToGlyphMap, c, fontUsed);
  -        if (*glyphUsed == nonGlyphID) {
  -            *glyphUsed = [renderer _extendUnicodeCharacterToGlyphMapToInclude:c];
  +        _glyphUsed = glyphForUnicodeCharacter(renderer->unicodeCharacterToGlyphMap, c, &_fontUsed);
  +        if (_glyphUsed == nonGlyphID) {
  +            _glyphUsed = [renderer _extendUnicodeCharacterToGlyphMapToInclude:c];
           }
       }
   
  @@ -2116,28 +2106,32 @@
       // ASSUMPTION:  We assume the same font in a smaller size has
       // the same glyphs as the large font.
       if (useSmallCapsFont) {
  -        if (*fontUsed == nil)
  -            *fontUsed = [renderer _smallCapsFont];
  +        if (_fontUsed == nil)
  +            _fontUsed = [renderer _smallCapsFont];
           else {
               // Potential for optimization.  This path should only be taken if we're
               // using a cached substituted font.
  -            *fontUsed = [[NSFontManager sharedFontManager] convertFont:*fontUsed toSize:[*fontUsed pointSize] * SMALLCAPS_FONTSIZE_MULTIPLIER];
  +            _fontUsed = [[NSFontManager sharedFontManager] convertFont:_fontUsed toSize:[_fontUsed pointSize] * SMALLCAPS_FONTSIZE_MULTIPLIER];
           }
       }
   
       // Now that we have glyph and font, get its width.
  -    WebGlyphWidth width = widthForGlyph(renderer, *glyphUsed, *fontUsed);
  +    WebGlyphWidth width;
  +    if (style->tabWidth != 0.0F && c == '\t') {
  +        width = style->tabWidth - fmodf(style->xpos+iterator->widthToStart+iterator->runWidthSoFar, style->tabWidth);
  +    } else {
  +        width = widthForGlyph(renderer, _glyphUsed, _fontUsed);
  +        // We special case spaces in two ways when applying word rounding.
  +        // First, we round spaces to an adjusted width in all fonts.
  +        // Second, in fixed-pitch fonts we ensure that all characters that
  +        // match the width of the space character have the same width as the space character.
  +        if (style->applyWordRounding && (renderer->treatAsFixedPitch ? width == renderer->spaceWidth : _glyphUsed == renderer->spaceGlyph))
  +            width = renderer->adjustedSpaceWidth;
  +    }
       
  -    // We special case spaces in two ways when applying word rounding.
  -    // First, we round spaces to an adjusted width in all fonts.
  -    // Second, in fixed-pitch fonts we ensure that all characters that
  -    // match the width of the space character have the same width as the space character.
  -    if ((renderer->treatAsFixedPitch ? width == renderer->spaceWidth : *glyphUsed == renderer->spaceGlyph) && iterator->style->applyWordRounding)
  -        width = renderer->adjustedSpaceWidth;
  -
       // Try to find a substitute font if this font didn't have a glyph for a character in the
       // string.  If one isn't found we end up drawing and measuring the 0 glyph, usually a box.
  -    if (*glyphUsed == 0 && iterator->style->attemptFontSubstitution) {
  +    if (_glyphUsed == 0 && style->attemptFontSubstitution) {
           UniChar characterArray[2];
           unsigned characterArrayLength;
           
  @@ -2151,7 +2145,7 @@
           }
           
           NSFont *substituteFont = [renderer _substituteFontForCharacters:characterArray length:characterArrayLength
  -            families:iterator->style->families];
  +            families:style->families];
           if (substituteFont) {
               int cNumGlyphs = 0;
               ATSGlyphRef localGlyphBuffer[MAX_GLYPH_EXPANSION];
  @@ -2174,8 +2168,8 @@
                               startPosition:nil
                               numGlyphs:&cNumGlyphs];
               
  -            *fontUsed = substituteFont;
  -            *glyphUsed = localGlyphBuffer[0];
  +            _fontUsed = substituteFont;
  +            _glyphUsed = localGlyphBuffer[0];
               
               if (c <= 0xFFFF && cNumGlyphs == 1 && localGlyphBuffer[0] != 0){
                   [renderer _updateGlyphEntryForCharacter:c glyphID:localGlyphBuffer[0] font:substituteFont];
  @@ -2183,18 +2177,18 @@
           }
       }
   
  -    if (!*fontUsed)
  -        *fontUsed = renderer->font;
  +    if (!_fontUsed)
  +        _fontUsed = renderer->font;
   
       // Force characters that are used to determine word boundaries for the rounding hack
       // to be integer width, so following words will start on an integer boundary.
  -    if (isRoundingHackCharacter(c) && iterator->style->applyWordRounding) {
  -        width = CEIL_TO_INT(width);
  +    if (style->applyWordRounding && isRoundingHackCharacter(c)) {
  +        width = ceilf(width);
       }
       
       // Account for letter-spacing
  -    if (iterator->style->letterSpacing && width > 0)
  -        width += iterator->style->letterSpacing;
  +    if (style->letterSpacing && width > 0)
  +        width += style->letterSpacing;
   
       // Account for padding.  khtml uses space padding to justify text.  We
       // distribute the specified padding over the available spaces in the run.
  @@ -2215,7 +2209,7 @@
           // Account for word-spacing.  We apply additional space between "words" by
           // adding width to the space character.
           if (currentCharacter > 0 && !isSpace(cp[-1]))
  -            width += iterator->style->wordSpacing;
  +            width += style->wordSpacing;
       }
   
       iterator->runWidthSoFar += width;
  @@ -2224,21 +2218,30 @@
       currentCharacter += clusterLength;
       iterator->currentCharacter = currentCharacter;
   
  -    int len = run->to - run->from;
  -
       // Account for float/integer impedance mismatch between CG and khtml.  "Words" (characters 
       // followed by a character defined by isSpace()) are always an integer width.  We adjust the 
       // width of the last character of a "word" to ensure an integer width.  When we move khtml to
       // floats we can remove this (and related) hacks.
       //
       // Check to see if the next character is a "RoundingHackCharacter", if so, adjust.
  -    if (currentCharacter < run->length && isRoundingHackCharacter(cp[clusterLength]) && iterator->style->applyWordRounding) {
  -        width += ceilCurrentWidth(iterator);
  -    }
  -    else if (currentCharacter >= (unsigned)run->to && (len > 1 || run->length == 1) && iterator->style->applyRunRounding) {
  -        width += ceilCurrentWidth(iterator);
  +    if (style->applyWordRounding && currentCharacter < run->length && isRoundingHackCharacter(cp[clusterLength])) {
  +        float delta = ceilCurrentWidth(iterator);
  +        if (delta)
  +            width += delta;
  +    } else {
  +        if (style->applyRunRounding && currentCharacter >= (unsigned)run->to && (run->length == 1 || run->to - run->from > 1)) {
  +            float delta = ceilCurrentWidth(iterator);
  +            if (delta)
  +                width += delta;
  +        }
       }
  +
  +    if (fontUsed)
  +        *fontUsed = _fontUsed;
       
  +    if (glyphUsed)
  +        *glyphUsed = _glyphUsed;
  +
       return width;
   }
   
  
  
  



More information about the webkit-changes mailing list