Skip to content

Loading…

Don't save the current context when drawing shadows. #345

Merged
merged 4 commits into from

2 participants

@rdougan

Honestly, I don't know enough to know exactly what is going on. But it seems that because drawInContext:context is being called for every shadow, and then again for the actual text.

I removed the Save/Restore context, and then draw the text and shadow all at once. It fixes #344 and after trying the DemoApp, everything looks fine.

@odrobnik

I don't believe that this is all of it. Is there maybe an unmatched save and restore somewhere?

@rdougan

Nope, they all match up.

@rdougan rdougan Do not drawInContext for each shadow. #344
`drawInContext:context` appears to draw the entire text for each shadow, which means if you have a shadow, the text will be drew twice. This causes strange antialiasing issues which makes the text looks weird.
699f3ac
@rdougan

Figured that the drawInContext is actually drawing the text each time as well as the shadow. Removing that too.

Do you know why this was originally added?

@odrobnik
@rdougan

OK, yeah I see. Multiple shadows are now broken.

@odrobnik
@odrobnik
@odrobnik
@rdougan

Using CGContextBeginTransparencyLayer seems to work:

if (shadows)
{
  CGContextBeginTransparencyLayer(context, nil);

  for (NSDictionary *shadowDict in shadows)
  {
    [self _setShadowInContext:context fromDictionary:shadowDict];
    [oneRun drawInContext:context];
  }

  CGContextEndTransparencyLayer(context);
}

Taken from here: http://stackoverflow.com/a/14114259/273985

@rdougan

However it does mention that it can be expensive.

@odrobnik
@rdougan

OK. Turns out that didn't work after all! Tried the offset method and that works.

@rdougan

I hate how dirty it is though.

if (shadows)
{
  CGContextSaveGState(context);
  CGContextSetTextPosition(context, textPosition.x + 10000, textPosition.y);

  for (NSDictionary *shadowDict in shadows)
  {
    [self _setShadowInContext:context fromDictionary:shadowDict];

    [oneRun drawInContext:context];
  }

  CGContextSetTextPosition(context, textPosition.x, textPosition.y);
  CGContextRestoreGState(context);
}

And then in _setShadowInContext:context:

offset.width -= 10000;

CGContextSetShadowWithColor(context, offset, blur, color.CGColor);
@rdougan rdougan Remove CGContextBeginTransparencyLayer for shadows. Instead, give tex…
…t an offset and then remove that same offset from the shadow, making it visible and the text not.
43386d1
@rdougan

Confirmed working in the DemoApp and in my testcase.

@odrobnik

I feel a bit uneasy with this myself, because theoretically there could be a very wide view/context and then the text would still be visible. We would have to clip to the area around the glyph run to avoid that.

Yet another suggestion was to use the same color as the shadow for drawing the text

in _setShadow...
CGContextSetShadowWithColor(context, offset, blur, color.CGColor);
CGContextSetFillColorWithColor(context, color.CGColor);

@odrobnik odrobnik merged commit f6db538 into Cocoanetics:master
@rdougan

Color unfortunately does not work. Same issue with the antialiasing of the font.

@odrobnik
@rdougan

Alrighty. Thanks!

@odrobnik odrobnik added a commit that referenced this pull request
@odrobnik odrobnik Optimize Shadow drawing
- Related to #345
- for text with no shadow only the glyph run is drawn
- for text with 1 shadow this is set and the glyph run is drawn
- for text with more than 1 shadow an area is clipped, 100 pixel wider in all directions
  - then the text position is moved outside that area and the shadow gets an additional offset in the other direction
  - this way only the shadow is drawn
  - except for the very last shadow, there the text position is moved back and the glyph run is drawn together with the last shadow
536321e
@odrobnik odrobnik was assigned
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Mar 14, 2013
  1. @rdougan
Commits on Mar 15, 2013
  1. @rdougan

    Do not drawInContext for each shadow. #344

    rdougan committed
    `drawInContext:context` appears to draw the entire text for each shadow, which means if you have a shadow, the text will be drew twice. This causes strange antialiasing issues which makes the text looks weird.
  2. @rdougan
  3. @rdougan

    Remove CGContextBeginTransparencyLayer for shadows. Instead, give tex…

    rdougan committed
    …t an offset and then remove that same offset from the shadow, making it visible and the text not.
Showing with 6 additions and 2 deletions.
  1. +6 −2 Core/Source/DTCoreTextLayoutFrame.m
View
8 Core/Source/DTCoreTextLayoutFrame.m
@@ -731,6 +731,7 @@ - (void)_setShadowInContext:(CGContextRef)context fromDictionary:(NSDictionary *
}
}
+ offset.width -= 10000;
CGContextSetShadowWithColor(context, offset, blur, color.CGColor);
}
@@ -1137,15 +1138,18 @@ - (void)drawInContext:(CGContextRef)context drawImages:(BOOL)drawImages drawLink
if (shadows)
{
CGContextSaveGState(context);
+
+ // Move the text 10000pt away from the current position so that on the shadow is visisble
+ CGContextSetTextPosition(context, textPosition.x + 10000, textPosition.y);
for (NSDictionary *shadowDict in shadows)
{
[self _setShadowInContext:context fromDictionary:shadowDict];
- // draw once per shadow
[oneRun drawInContext:context];
}
-
+
+ CGContextSetTextPosition(context, textPosition.x, textPosition.y);
CGContextRestoreGState(context);
}
Something went wrong with that request. Please try again.