GraphicsContextCG.cpp [plain text]
#include "config.h"
#include "GraphicsContext.h"
#include "Path.h"
using namespace std;
namespace WebCore {
static void setCGFillColor(CGContextRef context, const Color& color)
{
CGFloat red, green, blue, alpha;
color.getRGBA(red, green, blue, alpha);
CGContextSetRGBFillColor(context, red, green, blue, alpha);
}
static void setCGStrokeColor(CGContextRef context, const Color& color)
{
CGFloat red, green, blue, alpha;
color.getRGBA(red, green, blue, alpha);
CGContextSetRGBStrokeColor(context, red, green, blue, alpha);
}
void GraphicsContext::savePlatformState()
{
CGContextSaveGState(platformContext());
}
void GraphicsContext::restorePlatformState()
{
CGContextRestoreGState(platformContext());
}
void GraphicsContext::drawRect(const IntRect& rect)
{
if (paintingDisabled())
return;
CGContextRef context = platformContext();
if (fillColor().alpha()) {
setCGFillColor(context, fillColor());
CGContextFillRect(context, rect);
}
if (pen().style() != Pen::Pen::NoPen) {
setCGFillColor(context, pen().color());
GraphicsContext p(context);
CGRect ir= p.roundToDevicePixels(FloatRect(rect));
CGRect rects[4] = {
CGRectMake(ir.origin.x, ir.origin.y, ir.size.width, 1),
CGRectMake(ir.origin.x, ir.origin.y+ir.size.height - 1, ir.size.width, 1),
CGRectMake(ir.origin.x, ir.origin.y + 1, 1, ir.size.height - 2),
CGRectMake(ir.origin.x+ir.size.width - 1, ir.origin.y + 1, 1, ir.size.height - 2)
};
CGContextFillRects(context, rects, 4);
}
}
void GraphicsContext::drawLine(const IntPoint& point1, const IntPoint& point2, bool antialias)
{
if (paintingDisabled())
return;
Pen::PenStyle penStyle = pen().style();
if (penStyle == Pen::Pen::NoPen)
return;
float width = pen().width();
if (width < 1)
width = 1;
FloatPoint p1 = point1;
FloatPoint p2 = point2;
bool isVerticalLine = (p1.x() == p2.x());
if (penStyle == Pen::DotLine || penStyle == Pen::DashLine) {
if (isVerticalLine) {
p1.move(0, width);
p2.move(0, -width);
} else {
p1.move(width, 0);
p2.move(-width, 0);
}
}
if (((int)width) % 2) {
if (isVerticalLine) {
p1.move(0.5, 0);
p2.move(0.5, 0);
} else {
p1.move(0, 0.5);
p2.move(0, 0.5);
}
}
int patWidth = 0;
switch (penStyle) {
case Pen::NoPen:
case Pen::SolidLine:
break;
case Pen::DotLine:
patWidth = (int)width;
break;
case Pen::DashLine:
patWidth = 3 * (int)width;
break;
}
CGContextRef context = platformContext();
CGContextSaveGState(context);
setCGStrokeColor(context, pen().color());
CGContextSetShouldAntialias(context, antialias);
if (patWidth) {
setCGFillColor(context, pen().color());
if (isVerticalLine) {
CGContextFillRect(context, FloatRect(p1.x() - width / 2, p1.y() - width, width, width));
CGContextFillRect(context, FloatRect(p2.x() - width / 2, p2.y(), width, width));
} else {
CGContextFillRect(context, FloatRect(p1.x() - width, p1.y() - width / 2, width, width));
CGContextFillRect(context, FloatRect(p2.x(), p2.y() - width / 2, width, width));
}
int distance = (isVerticalLine ? (point2.y() - point1.y()) : (point2.x() - point1.x())) - 2*(int)width;
int remainder = distance % patWidth;
int coverage = distance - remainder;
int numSegments = coverage / patWidth;
float patternOffset = 0;
if (patWidth == 1)
patternOffset = 1.0;
else {
bool evenNumberOfSegments = numSegments % 2 == 0;
if (remainder)
evenNumberOfSegments = !evenNumberOfSegments;
if (evenNumberOfSegments) {
if (remainder) {
patternOffset += patWidth - remainder;
patternOffset += remainder / 2;
} else
patternOffset = patWidth / 2;
} else {
if (remainder)
patternOffset = (patWidth - remainder)/2;
}
}
const CGFloat dottedLine[2] = { patWidth, patWidth };
CGContextSetLineDash(context, patternOffset, dottedLine, 2);
}
CGContextSetLineWidth(context, width);
CGContextBeginPath(context);
CGContextMoveToPoint(context, p1.x(), p1.y());
CGContextAddLineToPoint(context, p2.x(), p2.y());
CGContextStrokePath(context);
CGContextRestoreGState(context);
}
void GraphicsContext::drawJoinedLines(CGPoint points [], unsigned count, bool antialias, CGLineCap lineCap)
{
if (paintingDisabled() || !count)
return;
float width = pen().width();
CGContextRef context = platformContext();
CGContextSaveGState(context);
setCGStrokeColor(context, pen().color());
CGContextSetShouldAntialias(context, antialias);
CGContextSetLineWidth(context, width < 1 ? 1 : width);
CGContextBeginPath(context);
CGContextSetLineCap(context, lineCap);
CGContextMoveToPoint(context, points[0].x, points[0].y);
for (unsigned i = 1; i < count; ++i)
CGContextAddLineToPoint(context, points[i].x, points[i].y);
CGContextStrokePath(context);
CGContextRestoreGState(context);
}
void GraphicsContext::drawEllipse(const IntRect& rect)
{
ASSERT(rect.width() == rect.height());
if (paintingDisabled())
return;
CGContextRef context = platformContext();
CGContextBeginPath(context);
float r = (float)rect.width() / 2;
CGContextAddArc(context, rect.x() + r, rect.y() + r, r, 0, 2*M_PI, true);
CGContextClosePath(context);
if (fillColor().alpha()) {
setCGFillColor(context, fillColor());
if (pen().style() != Pen::NoPen) {
setCGStrokeColor(context, pen().color());
unsigned penWidth = pen().width();
if (penWidth == 0)
penWidth++;
CGContextSetLineWidth(context, penWidth);
CGContextDrawPath(context, kCGPathFillStroke);
} else
CGContextFillPath(context);
} else if (pen().style() != Pen::NoPen) {
setCGStrokeColor(context, pen().color());
unsigned penWidth = pen().width();
if (penWidth == 0)
penWidth++;
CGContextSetLineWidth(context, penWidth);
CGContextStrokePath(context);
}
}
void GraphicsContext::drawEllipse(const FloatRect& rect)
{
if (paintingDisabled())
return;
CGContextRef context(platformContext());
CGContextSaveGState(context);
setCGFillColor(context, fillColor());
setCGStrokeColor(context, pen().color());
CGContextSetLineWidth(context, pen().width());
CGContextBeginPath(context);
CGContextAddEllipseInRect(context, rect);
CGContextFillPath(context);
CGContextStrokePath(context);
CGContextRestoreGState(context);
}
void GraphicsContext::drawArc(const IntRect& rect, float thickness, int startAngle, int angleSpan)
{
if (paintingDisabled())
return;
CGContextRef context = platformContext();
CGContextSaveGState(context);
CGContextBeginPath(context);
CGContextSetShouldAntialias(context, false);
int x = rect.x();
int y = rect.y();
float w = (float)rect.width();
float h = (float)rect.height();
float scaleFactor = h / w;
float reverseScaleFactor = w / h;
if (w != h)
scale(FloatSize(1, scaleFactor));
float hRadius = w / 2;
float vRadius = h / 2;
float fa = startAngle;
float falen = fa + angleSpan;
float start = -fa * M_PI/180;
float end = -falen * M_PI/180;
CGContextAddArc(context, x + hRadius, (y + vRadius) * reverseScaleFactor, hRadius, start, end, true);
if (w != h)
scale(FloatSize(1, reverseScaleFactor));
if (pen().style() == Pen::NoPen) {
setCGStrokeColor(context, fillColor());
CGContextSetLineWidth(context, thickness);
CGContextStrokePath(context);
} else {
Pen::PenStyle penStyle = pen().style();
float width = pen().width();
if (width < 1)
width = 1;
int patWidth = 0;
switch (penStyle) {
case Pen::NoPen:
case Pen::SolidLine:
break;
case Pen::DotLine:
patWidth = (int)(width / 2);
break;
case Pen::DashLine:
patWidth = 3 * (int)(width / 2);
break;
}
CGContextSaveGState(context);
setCGStrokeColor(context, pen().color());
if (patWidth) {
int distance;
if (hRadius == vRadius)
distance = (int)(M_PI * hRadius) / 2;
else distance = (int)(M_PI * sqrt((hRadius * hRadius + vRadius * vRadius) / 2)) / 2;
int remainder = distance % patWidth;
int coverage = distance - remainder;
int numSegments = coverage / patWidth;
float patternOffset = 0;
if (patWidth == 1)
patternOffset = 1.0;
else {
bool evenNumberOfSegments = numSegments % 2 == 0;
if (remainder)
evenNumberOfSegments = !evenNumberOfSegments;
if (evenNumberOfSegments) {
if (remainder) {
patternOffset += patWidth - remainder;
patternOffset += remainder / 2;
} else
patternOffset = patWidth / 2;
} else {
if (remainder)
patternOffset = (patWidth - remainder) / 2;
}
}
const CGFloat dottedLine[2] = { patWidth, patWidth };
CGContextSetLineDash(context, patternOffset, dottedLine, 2);
}
CGContextSetLineWidth(context, width);
CGContextStrokePath(context);
CGContextRestoreGState(context);
}
CGContextRestoreGState(context);
}
void GraphicsContext::drawConvexPolygon(size_t npoints, const FloatPoint* points, bool shouldAntialias)
{
if (paintingDisabled())
return;
if (npoints <= 1)
return;
CGContextRef context = platformContext();
CGContextSaveGState(context);
CGContextSetShouldAntialias(context, shouldAntialias);
CGContextBeginPath(context);
CGContextMoveToPoint(context, points[0].x(), points[0].y());
for (size_t i = 1; i < npoints; i++)
CGContextAddLineToPoint(context, points[i].x(), points[i].y());
CGContextClosePath(context);
if (fillColor().alpha()) {
setCGFillColor(context, fillColor());
CGContextEOFillPath(context);
}
if (pen().style() != Pen::NoPen) {
setCGStrokeColor(context, pen().color());
CGContextSetLineWidth(context, pen().width());
CGContextStrokePath(context);
}
CGContextRestoreGState(context);
}
void GraphicsContext::fillRect(const IntRect& rect, const Color& color)
{
if (paintingDisabled())
return;
if (color.alpha()) {
CGContextRef context = platformContext();
setCGFillColor(context, color);
CGContextFillRect(context, rect);
}
}
void GraphicsContext::fillRect(const FloatRect& rect, const Color& color)
{
if (paintingDisabled())
return;
if (color.alpha()) {
CGContextRef context = platformContext();
setCGFillColor(context, color);
CGContextFillRect(context, rect);
}
}
void GraphicsContext::addClip(const IntRect& rect)
{
if (paintingDisabled())
return;
CGContextClipToRect(platformContext(), rect);
}
void GraphicsContext::addRoundedRectClip(const IntRect& rect, const IntSize& topLeft, const IntSize& topRight,
const IntSize& bottomLeft, const IntSize& bottomRight)
{
if (paintingDisabled())
return;
int requiredWidth = max(topLeft.width() + topRight.width(), bottomLeft.width() + bottomRight.width());
if (requiredWidth > rect.width())
return;
int requiredHeight = max(topLeft.height() + bottomLeft.height(), topRight.height() + bottomRight.height());
if (requiredHeight > rect.height())
return;
addClip(rect);
CGContextRef context = platformContext();
CGContextAddEllipseInRect(context, CGRectMake(rect.x(), rect.y(), topLeft.width() * 2, topLeft.height() * 2));
CGContextAddEllipseInRect(context, CGRectMake(rect.right() - topRight.width() * 2, rect.y(),
topRight.width() * 2, topRight.height() * 2));
CGContextAddEllipseInRect(context, CGRectMake(rect.x(), rect.bottom() - bottomLeft.height() * 2,
bottomLeft.width() * 2, bottomLeft.height() * 2));
CGContextAddEllipseInRect(context, CGRectMake(rect.right() - bottomRight.width() * 2,
rect.bottom() - bottomRight.height() * 2,
bottomRight.width() * 2, bottomRight.height() * 2));
CGContextAddRect(context, CGRectMake(rect.x() + topLeft.width(), rect.y(),
rect.width() - topLeft.width() - topRight.width(),
max(topLeft.height(), topRight.height())));
CGContextAddRect(context, CGRectMake(rect.x() + bottomLeft.width(),
rect.bottom() - max(bottomLeft.height(), bottomRight.height()),
rect.width() - bottomLeft.width() - bottomRight.width(),
max(bottomLeft.height(), bottomRight.height())));
CGContextAddRect(context, CGRectMake(rect.x(), rect.y() + topLeft.height(),
max(topLeft.width(), bottomLeft.width()), rect.height() - topLeft.height() - bottomLeft.height()));
CGContextAddRect(context, CGRectMake(rect.right() - max(topRight.width(), bottomRight.width()),
rect.y() + topRight.height(),
max(topRight.width(), bottomRight.width()), rect.height() - topRight.height() - bottomRight.height()));
CGContextAddRect(context, CGRectMake(rect.x() + max(topLeft.width(), bottomLeft.width()),
rect.y() + max(topLeft.height(), topRight.height()),
rect.width() - max(topLeft.width(), bottomLeft.width()) - max(topRight.width(), bottomRight.width()),
rect.height() - max(topLeft.height(), topRight.height()) - max(bottomLeft.height(), bottomRight.height())));
CGContextClip(context);
}
void GraphicsContext::addRoundedRectClip(const FloatRect& rect, const FloatSize& topLeft, const FloatSize& topRight,
const FloatSize& bottomLeft, const FloatSize& bottomRight)
{
if (paintingDisabled())
return;
int requiredWidth = max(topLeft.width() + topRight.width(), bottomLeft.width() + bottomRight.width());
if (requiredWidth > rect.width())
return;
int requiredHeight = max(topLeft.height() + bottomLeft.height(), topRight.height() + bottomRight.height());
if (requiredHeight > rect.height())
return;
clip(rect);
CGContextRef context = platformContext();
CGContextAddEllipseInRect(context, CGRectMake(rect.x(), rect.y(), topLeft.width() * 2, topLeft.height() * 2));
CGContextAddEllipseInRect(context, CGRectMake(rect.right() - topRight.width() * 2, rect.y(),
topRight.width() * 2, topRight.height() * 2));
CGContextAddEllipseInRect(context, CGRectMake(rect.x(), rect.bottom() - bottomLeft.height() * 2,
bottomLeft.width() * 2, bottomLeft.height() * 2));
CGContextAddEllipseInRect(context, CGRectMake(rect.right() - bottomRight.width() * 2,
rect.bottom() - bottomRight.height() * 2,
bottomRight.width() * 2, bottomRight.height() * 2));
CGContextAddRect(context, CGRectMake(rect.x() + topLeft.width(), rect.y(),
rect.width() - topLeft.width() - topRight.width(),
max(topLeft.height(), topRight.height())));
CGContextAddRect(context, CGRectMake(rect.x() + bottomLeft.width(),
rect.bottom() - max(bottomLeft.height(), bottomRight.height()),
rect.width() - bottomLeft.width() - bottomRight.width(),
max(bottomLeft.height(), bottomRight.height())));
CGContextAddRect(context, CGRectMake(rect.x(), rect.y() + topLeft.height(),
max(topLeft.width(), bottomLeft.width()), rect.height() - topLeft.height() - bottomLeft.height()));
CGContextAddRect(context, CGRectMake(rect.right() - max(topRight.width(), bottomRight.width()),
rect.y() + topRight.height(),
max(topRight.width(), bottomRight.width()), rect.height() - topRight.height() - bottomRight.height()));
CGContextAddRect(context, CGRectMake(rect.x() + max(topLeft.width(), bottomLeft.width()),
rect.y() + max(topLeft.height(), topRight.height()),
rect.width() - max(topLeft.width(), bottomLeft.width()) - max(topRight.width(), bottomRight.width()),
rect.height() - max(topLeft.height(), topRight.height()) - max(bottomLeft.height(), bottomRight.height())));
CGContextClip(context);
}
void GraphicsContext::addInnerRoundedRectClip(const IntRect& rect, int thickness)
{
if (paintingDisabled())
return;
addClip(rect);
CGContextRef context = platformContext();
CGContextAddEllipseInRect(context, CGRectMake(rect.x(), rect.y(), rect.width(), rect.height()));
CGContextAddEllipseInRect(context, CGRectMake(rect.x() + thickness, rect.y() + thickness,
rect.width() - (thickness * 2), rect.height() - (thickness * 2)));
CGContextEOClip(context);
}
#if SVG_SUPPORT
KRenderingDeviceContext* GraphicsContext::createRenderingDeviceContext()
{
return new KRenderingDeviceContextQuartz(platformContext());
}
#endif
void GraphicsContext::beginTransparencyLayer(float opacity)
{
if (paintingDisabled())
return;
CGContextRef context = platformContext();
CGContextSaveGState(context);
CGContextSetAlpha(context, opacity);
CGContextBeginTransparencyLayer(context, 0);
}
void GraphicsContext::endTransparencyLayer()
{
if (paintingDisabled())
return;
CGContextRef context = platformContext();
CGContextEndTransparencyLayer(context);
CGContextRestoreGState(context);
}
void GraphicsContext::setShadow(const IntSize& size, int blur, const Color& color)
{
if (paintingDisabled())
return;
CGContextRef context = platformContext();
if (!color.isValid())
CGContextSetShadow(context, CGSizeMake(size.width(), -size.height()), blur); else {
CGColorRef colorCG = cgColor(color);
CGContextSetShadowWithColor(context,
CGSizeMake(size.width(), -size.height()), blur,
colorCG);
CGColorRelease(colorCG);
}
}
void GraphicsContext::clearShadow()
{
if (paintingDisabled())
return;
CGContextSetShadowWithColor(platformContext(), CGSizeZero, 0, 0);
}
void GraphicsContext::setLineWidth(float width)
{
if (paintingDisabled())
return;
CGContextSetLineWidth(platformContext(), width);
}
void GraphicsContext::setMiterLimit(float limit)
{
if (paintingDisabled())
return;
CGContextSetMiterLimit(platformContext(), limit);
}
void GraphicsContext::setAlpha(float alpha)
{
if (paintingDisabled())
return;
CGContextSetAlpha(platformContext(), alpha);
}
void GraphicsContext::clearRect(const FloatRect& r)
{
if (paintingDisabled())
return;
CGContextClearRect(platformContext(), r);
}
void GraphicsContext::strokeRect(const FloatRect& r, float lineWidth)
{
if (paintingDisabled())
return;
CGContextStrokeRectWithWidth(platformContext(), r, lineWidth);
}
void GraphicsContext::setLineCap(LineCap cap)
{
if (paintingDisabled())
return;
switch (cap) {
case ButtCap:
CGContextSetLineCap(platformContext(), kCGLineCapButt);
break;
case RoundCap:
CGContextSetLineCap(platformContext(), kCGLineCapRound);
break;
case SquareCap:
CGContextSetLineCap(platformContext(), kCGLineCapSquare);
break;
}
}
void GraphicsContext::setLineJoin(LineJoin join)
{
if (paintingDisabled())
return;
switch (join) {
case MiterJoin:
CGContextSetLineJoin(platformContext(), kCGLineJoinMiter);
break;
case RoundJoin:
CGContextSetLineJoin(platformContext(), kCGLineJoinRound);
break;
case BevelJoin:
CGContextSetLineJoin(platformContext(), kCGLineJoinBevel);
break;
}
}
void GraphicsContext::clip(const Path& path)
{
if (paintingDisabled())
return;
CGContextRef context = platformContext();
CGContextBeginPath(context);
CGContextAddPath(context, path.platformPath());
CGContextClip(context);
}
void GraphicsContext::clip(const FloatRect& rect)
{
if (paintingDisabled())
return;
CGContextClipToRect(platformContext(), rect);
}
void GraphicsContext::scale(const FloatSize& size)
{
if (paintingDisabled())
return;
CGContextScaleCTM(platformContext(), size.width(), size.height());
}
void GraphicsContext::rotate(float angle)
{
if (paintingDisabled())
return;
CGContextRotateCTM(platformContext(), angle);
}
void GraphicsContext::translate(const FloatSize& size)
{
if (paintingDisabled())
return;
CGContextTranslateCTM(platformContext(), size.width(), size.height());
}
static void InterpolateLinearGradient(void *info, const CGFloat *inData, CGFloat *outData)
{
float alpha = inData[0],
inverse = 1.0 - alpha;
outData[0] = inverse * static_cast<GradientRef>(info)->start[0] + alpha * static_cast<GradientRef>(info)->end[0];
outData[1] = inverse * static_cast<GradientRef>(info)->start[1] + alpha * static_cast<GradientRef>(info)->end[1];
outData[2] = inverse * static_cast<GradientRef>(info)->start[2] + alpha * static_cast<GradientRef>(info)->end[2];
outData[3] = inverse * static_cast<GradientRef>(info)->start[3] + alpha * static_cast<GradientRef>(info)->end[3];
}
static void InterpolateExponentialGradient(void *info, const CGFloat *inData, CGFloat *outData)
{
int i = 0;
float a = inData[0];
for(; i < 4; ++i) {
float end = log(MAX(static_cast<GradientRef>(info)->end[i], 0.01)),
start = log(MAX(static_cast<GradientRef>(info)->start[i], 0.01));
outData[i] = exp(start - (end + start) * a);
}
}
static CGFunctionRef GetSharedFunctionRef(GradientRef aGradient, Interpolation anInterpolation)
{
CGFunctionRef function = NULL;
static HashMap<GradientRef, CGFunctionRef> * linearFunctionRefs = NULL;
static HashMap<GradientRef, CGFunctionRef> * exponentialFunctionRefs = NULL;
if (anInterpolation == LinearInterpolation) {
if (!linearFunctionRefs) linearFunctionRefs = new HashMap<GradientRef, CGFunctionRef>;
else function = linearFunctionRefs->get(aGradient);
if (!function) {
static struct CGFunctionCallbacks linearFunctionCallbacks = { 0, InterpolateLinearGradient, NULL };
linearFunctionRefs->set(aGradient, function = CGFunctionCreate(aGradient, 1, NULL, 4, NULL, &linearFunctionCallbacks));
}
return function;
}
else {
if (!exponentialFunctionRefs) exponentialFunctionRefs = new HashMap<GradientRef, CGFunctionRef>;
else function = exponentialFunctionRefs->get(aGradient);
if (!function) {
static struct CGFunctionCallbacks exponentialFunctionCallbacks = { 0, InterpolateExponentialGradient, NULL };
exponentialFunctionRefs->set(aGradient, function = CGFunctionCreate(aGradient, 1, NULL, 4, NULL, &exponentialFunctionCallbacks));
}
return function;
}
}
static CGColorSpaceRef GetSharedColorSpace()
{
CGColorSpaceRef sharedColorSpace = NULL;
if (!sharedColorSpace) sharedColorSpace = CGColorSpaceCreateDeviceRGB();
return sharedColorSpace;
}
void GraphicsContext::drawAxialGradient(GradientRef aGradient, FloatPoint startPoint, FloatPoint stopPoint, Interpolation anInterpolation)
{
CGShadingRef shading = CGShadingCreateAxial(GetSharedColorSpace(), startPoint, stopPoint, GetSharedFunctionRef(aGradient, anInterpolation), false, false);
CGContextDrawShading(platformContext(), shading);
CFRelease(shading);
}
void GraphicsContext::drawRadialGradient(GradientRef aGradient, FloatPoint startPoint, float startRadius, FloatPoint stopPoint, float stopRadius, Interpolation anInterpolation)
{
CGShadingRef shading = CGShadingCreateRadial(GetSharedColorSpace(), startPoint, startRadius, stopPoint, stopRadius, GetSharedFunctionRef(aGradient, anInterpolation), false, false);
CGContextDrawShading(platformContext(), shading);
CFRelease(shading);
}
FloatRect GraphicsContext::roundToDevicePixels(const FloatRect& rect)
{
CGAffineTransform deviceMatrix = CGContextGetUserSpaceToDeviceSpaceTransform(platformContext());
float deviceScaleX = sqrtf(deviceMatrix.a * deviceMatrix.a + deviceMatrix.b * deviceMatrix.b);
float deviceScaleY = sqrtf(deviceMatrix.c * deviceMatrix.c + deviceMatrix.d * deviceMatrix.d);
CGPoint deviceOrigin = CGPointMake(rect.x() * deviceScaleX, rect.y() * deviceScaleY);
CGPoint deviceLowerRight = CGPointMake((rect.x() + rect.width()) * deviceScaleX,
(rect.y() + rect.height()) * deviceScaleY);
deviceOrigin.x = roundf(deviceOrigin.x);
deviceOrigin.y = roundf(deviceOrigin.y);
deviceLowerRight.x = roundf(deviceLowerRight.x);
deviceLowerRight.y = roundf(deviceLowerRight.y);
if (deviceOrigin.y == deviceLowerRight.y && rect.height() != 0)
deviceLowerRight.y += 1;
if (deviceOrigin.x == deviceLowerRight.x && rect.width() != 0)
deviceLowerRight.x += 1;
FloatPoint roundedOrigin = FloatPoint(deviceOrigin.x / deviceScaleX, deviceOrigin.y / deviceScaleY);
FloatPoint roundedLowerRight = FloatPoint(deviceLowerRight.x / deviceScaleX, deviceLowerRight.y / deviceScaleY);
return FloatRect(roundedOrigin, roundedLowerRight - roundedOrigin);
}
static CGPoint roundPointToDevicePixels(CGContextRef context, CGPoint p, int offset)
{
CGAffineTransform deviceMatrix = CGContextGetUserSpaceToDeviceSpaceTransform(context);
float s = deviceMatrix.a;
float x = roundf(p.x * s);
float y = ceilf((p.y * s) + (offset * s));
static const float OffsetUnderlineScale = 0.4;
float dy = s < OffsetUnderlineScale ? 0 : 1;
return CGPointMake(x / s, (y + dy) / s);
}
void GraphicsContext::drawLineForText(const IntPoint& point, int yOffset, int width, bool printing)
{
if (paintingDisabled())
return;
float x = point.x();
float y = point.y() + yOffset;
y += 1;
float thickness = pen().width();
if (printing) {
if (thickness < 0.5)
thickness = 0.5;
} else {
}
CGContextRef context = platformContext();
CGContextSaveGState(context);
CGContextSetLineWidth(context, 1);
CGContextSetShouldAntialias(context, 0);
CGAffineTransform t = CGContextGetCTM(context);
float scale = t.a;
Color color(pen().color());
if (scale < 1.0) {
static const float MinUnderlineAlpha = 0.4;
float shade = scale > MinUnderlineAlpha ? scale : MinUnderlineAlpha;
int alpha = color.alpha() * shade;
color = Color(color.red(), color.green(), color.blue(), alpha);
}
setCGFillColor(context, color);
CGPoint origin = roundPointToDevicePixels(context, CGPointMake(x, point.y()), yOffset);
CGRect rect = CGRectMake(origin.x, origin.y, width, 1); CGContextFillRect(context, rect);
CGContextRestoreGState(context);
}
}