GraphicsContextQt.cpp [plain text]
#include "config.h"
#include "GraphicsContext.h"
#if OS(WINDOWS)
#include <windows.h>
#endif
#include "AffineTransform.h"
#include "Color.h"
#include "FloatConversion.h"
#include "Font.h"
#include "ImageBuffer.h"
#include "NotImplemented.h"
#include "Path.h"
#include "Pattern.h"
#include "ShadowBlur.h"
#include "TransformationMatrix.h"
#include "TransparencyLayer.h"
#include <QBrush>
#include <QGradient>
#include <QPaintDevice>
#include <QPaintEngine>
#include <QPainter>
#include <QPainterPath>
#include <QPainterPathStroker>
#include <QPixmap>
#include <QPolygonF>
#include <QStack>
#include <QVector>
#include <wtf/MathExtras.h>
#if OS(WINDOWS)
QT_BEGIN_NAMESPACE
Q_GUI_EXPORT QPixmap qt_pixmapFromWinHBITMAP(HBITMAP, int hbitmapFormat = 0);
QT_END_NAMESPACE
enum HBitmapFormat {
HBitmapNoAlpha,
HBitmapPremultipliedAlpha,
HBitmapAlpha
};
#endif
namespace WebCore {
static inline QPainter::CompositionMode toQtCompositionMode(CompositeOperator op)
{
switch (op) {
case CompositeClear:
return QPainter::CompositionMode_Clear;
case CompositeCopy:
return QPainter::CompositionMode_Source;
case CompositeSourceOver:
return QPainter::CompositionMode_SourceOver;
case CompositeSourceIn:
return QPainter::CompositionMode_SourceIn;
case CompositeSourceOut:
return QPainter::CompositionMode_SourceOut;
case CompositeSourceAtop:
return QPainter::CompositionMode_SourceAtop;
case CompositeDestinationOver:
return QPainter::CompositionMode_DestinationOver;
case CompositeDestinationIn:
return QPainter::CompositionMode_DestinationIn;
case CompositeDestinationOut:
return QPainter::CompositionMode_DestinationOut;
case CompositeDestinationAtop:
return QPainter::CompositionMode_DestinationAtop;
case CompositeXOR:
return QPainter::CompositionMode_Xor;
case CompositePlusDarker:
return QPainter::CompositionMode_Darken;
case CompositePlusLighter:
return QPainter::CompositionMode_Plus;
case CompositeDifference:
return QPainter::CompositionMode_Difference;
default:
ASSERT_NOT_REACHED();
}
return QPainter::CompositionMode_SourceOver;
}
static inline Qt::PenCapStyle toQtLineCap(LineCap lc)
{
switch (lc) {
case ButtCap:
return Qt::FlatCap;
case RoundCap:
return Qt::RoundCap;
case SquareCap:
return Qt::SquareCap;
default:
ASSERT_NOT_REACHED();
}
return Qt::FlatCap;
}
static inline Qt::PenJoinStyle toQtLineJoin(LineJoin lj)
{
switch (lj) {
case MiterJoin:
return Qt::SvgMiterJoin;
case RoundJoin:
return Qt::RoundJoin;
case BevelJoin:
return Qt::BevelJoin;
default:
ASSERT_NOT_REACHED();
}
return Qt::SvgMiterJoin;
}
static Qt::PenStyle toQPenStyle(StrokeStyle style)
{
switch (style) {
case NoStroke:
return Qt::NoPen;
break;
case SolidStroke:
#if ENABLE(CSS3_TEXT)
case DoubleStroke:
case WavyStroke:
#endif
return Qt::SolidLine;
break;
case DottedStroke:
return Qt::DotLine;
break;
case DashedStroke:
return Qt::DashLine;
break;
default:
ASSERT_NOT_REACHED();
}
return Qt::NoPen;
}
static inline Qt::FillRule toQtFillRule(WindRule rule)
{
switch (rule) {
case RULE_EVENODD:
return Qt::OddEvenFill;
case RULE_NONZERO:
return Qt::WindingFill;
default:
ASSERT_NOT_REACHED();
}
return Qt::OddEvenFill;
}
static inline void adjustPointsForDottedLine(FloatPoint& p1, FloatPoint& p2, float width, bool isVerticalLine)
{
if (isVerticalLine) {
p1.setY(p1.y() - width / 2);
p2.setY(p2.y() + width / 2);
} else {
p1.setX(p1.x() - width / 2);
p2.setX(p2.x() + width / 2);
}
}
static inline void drawLineEndpointsForStyle(QPainter *painter, const FloatPoint& p1, const FloatPoint& p2, float width, bool isVerticalLine, StrokeStyle style, Color color)
{
if (style == DashedStroke) {
if (isVerticalLine) {
painter->fillRect(FloatRect(p1.x() - width / 2, p1.y() - width, width, width), QColor(color));
painter->fillRect(FloatRect(p2.x() - width / 2, p2.y(), width, width), QColor(color));
} else {
painter->fillRect(FloatRect(p1.x() - width, p1.y() - width / 2, width, width), QColor(color));
painter->fillRect(FloatRect(p2.x(), p2.y() - width / 2, width, width), QColor(color));
}
}
if (style == DottedStroke) {
painter->setPen(Qt::NoPen);
painter->setBrush(QColor(color));
painter->drawEllipse(p1.x() - width / 2, p1.y() - width / 2, width, width);
painter->drawEllipse(p2.x() - width / 2, p2.y() - width / 2, width, width);
}
}
class GraphicsContextPlatformPrivate {
WTF_MAKE_NONCOPYABLE(GraphicsContextPlatformPrivate); WTF_MAKE_FAST_ALLOCATED;
public:
GraphicsContextPlatformPrivate(QPainter*, const QColor& initialSolidColor);
~GraphicsContextPlatformPrivate();
inline QPainter* p() const
{
if (layers.isEmpty())
return painter;
return &layers.top()->painter;
}
bool antiAliasingForRectsAndLines;
QStack<TransparencyLayer*> layers;
int layerCount;
QBrush solidColor;
InterpolationQuality imageInterpolationQuality;
bool initialSmoothPixmapTransformHint;
QRectF clipBoundingRect() const
{
return p()->clipBoundingRect();
}
void takeOwnershipOfPlatformContext() { platformContextIsOwned = true; }
private:
QPainter* painter;
bool platformContextIsOwned;
};
GraphicsContextPlatformPrivate::GraphicsContextPlatformPrivate(QPainter* p, const QColor& initialSolidColor)
: antiAliasingForRectsAndLines(false)
, layerCount(0)
, solidColor(initialSolidColor)
, imageInterpolationQuality(InterpolationDefault)
, initialSmoothPixmapTransformHint(false)
, painter(p)
, platformContextIsOwned(false)
{
if (!painter)
return;
antiAliasingForRectsAndLines = painter->testRenderHint(QPainter::Antialiasing);
initialSmoothPixmapTransformHint = painter->testRenderHint(QPainter::SmoothPixmapTransform);
painter->setRenderHint(QPainter::Antialiasing, true);
}
GraphicsContextPlatformPrivate::~GraphicsContextPlatformPrivate()
{
if (!platformContextIsOwned)
return;
QPaintDevice* device = painter->device();
painter->end();
delete painter;
delete device;
}
void GraphicsContext::platformInit(PlatformGraphicsContext* painter)
{
m_data = new GraphicsContextPlatformPrivate(painter, fillColor());
setPaintingDisabled(!painter);
if (!painter)
return;
painter->setBrush(m_data->solidColor);
QPen pen(painter->pen());
pen.setColor(strokeColor());
pen.setJoinStyle(toQtLineJoin(MiterJoin));
painter->setPen(pen);
}
void GraphicsContext::platformDestroy()
{
while (!m_data->layers.isEmpty())
endTransparencyLayer();
delete m_data;
}
PlatformGraphicsContext* GraphicsContext::platformContext() const
{
return m_data->p();
}
AffineTransform GraphicsContext::getCTM(IncludeDeviceScale) const
{
if (paintingDisabled())
return AffineTransform();
const QTransform& matrix = platformContext()->combinedTransform();
return AffineTransform(matrix.m11(), matrix.m12(), matrix.m21(),
matrix.m22(), matrix.dx(), matrix.dy());
}
void GraphicsContext::savePlatformState()
{
if (!m_data->layers.isEmpty() && !m_data->layers.top()->alphaMask.isNull())
++m_data->layers.top()->saveCounter;
m_data->p()->save();
}
void GraphicsContext::restorePlatformState()
{
if (!m_data->layers.isEmpty() && !m_data->layers.top()->alphaMask.isNull())
if (!--m_data->layers.top()->saveCounter)
endPlatformTransparencyLayer();
m_data->p()->restore();
}
void GraphicsContext::drawRect(const IntRect& rect)
{
if (paintingDisabled())
return;
ASSERT(!rect.isEmpty());
QPainter* p = m_data->p();
const bool antiAlias = p->testRenderHint(QPainter::Antialiasing);
p->setRenderHint(QPainter::Antialiasing, m_data->antiAliasingForRectsAndLines);
p->drawRect(rect);
p->setRenderHint(QPainter::Antialiasing, antiAlias);
}
void GraphicsContext::drawLine(const IntPoint& point1, const IntPoint& point2)
{
if (paintingDisabled())
return;
StrokeStyle style = strokeStyle();
Color color = strokeColor();
if (style == NoStroke)
return;
float width = strokeThickness();
FloatPoint p1 = point1;
FloatPoint p2 = point2;
bool isVerticalLine = (p1.x() == p2.x());
QPainter* p = m_data->p();
const bool antiAlias = p->testRenderHint(QPainter::Antialiasing);
p->setRenderHint(QPainter::Antialiasing, m_data->antiAliasingForRectsAndLines);
adjustLineToPixelBoundaries(p1, p2, width, style);
Qt::PenCapStyle capStyle = Qt::FlatCap;
QVector<qreal> dashes;
int patWidth = 0;
switch (style) {
case NoStroke:
case SolidStroke:
#if ENABLE(CSS3_TEXT)
case DoubleStroke:
case WavyStroke:
#endif
break;
case DottedStroke: {
capStyle = Qt::RoundCap;
patWidth = static_cast<int>(width);
const qreal lineElementLength = 0.01;
dashes << lineElementLength << qreal(2 * patWidth) / width - lineElementLength;
adjustPointsForDottedLine(p1, p2, width, isVerticalLine);
break;
}
case DashedStroke:
capStyle = Qt::FlatCap;
patWidth = 3 * static_cast<int>(width);
dashes << qreal(patWidth) / width << qreal(patWidth) / width;
break;
}
if (patWidth) {
p->save();
QPen pen = p->pen();
drawLineEndpointsForStyle(p, p1, p2, width, isVerticalLine, style, color);
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.0f;
if (patWidth == 1)
patternOffset = 1.0f;
else {
bool evenNumberOfSegments = !(numSegments % 2);
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;
}
}
pen.setWidthF(width);
pen.setCapStyle(capStyle);
pen.setDashPattern(dashes);
pen.setDashOffset(patternOffset / width);
p->setPen(pen);
}
#if ENABLE(CSS3_TEXT)
if (style == WavyStroke) {
const float step = 2 * width; const float flat = width; short signal = -1;
QPainterPath path;
float x1, y1, x2, y2;
if (isVerticalLine) {
x1 = x2 = p1.x();
if (p1.y() < p2.y()) {
y1 = p1.y();
y2 = p2.y();
} else {
y1 = p2.y();
y2 = p1.y();
}
y2 -= 1;
path.moveTo(x1 + signal * step, y1);
float y = y1 + 2 * step;
while (y <= y2) {
signal = -signal;
path.lineTo(x1 + signal * step, y);
path.lineTo(x1 + signal * step, y + flat); y += 2 * step + flat;
}
} else {
y1 = y2 = p1.y();
if (p1.x() < p2.x()) {
x1 = p1.x();
x2 = p2.x();
} else {
x1 = p2.x();
x2 = p1.x();
}
x2 -= 1;
path.moveTo(x1, y1 + signal * step);
float x = x1 + 2 * step;
while (x <= x2) {
signal = -signal;
path.lineTo(x, y1 + signal * step);
path.lineTo(x + flat, y1 + signal * step); x += 2 * step + flat;
}
}
path.lineTo(x2, y2);
QPen pen = p->pen();
pen.setJoinStyle(Qt::BevelJoin); pen.setWidth(width);
const bool oldAntiAliasing = p->testRenderHint(QPainter::Antialiasing);
p->setRenderHint(QPainter::Antialiasing, true); p->strokePath(path, pen);
p->setRenderHint(QPainter::Antialiasing, oldAntiAliasing);
} else {
#endif // CSS3_TEXT
if (p1.x() == p2.x())
p->drawLine(p1, p2 - FloatSize(0, 1));
else
p->drawLine(p1, p2 - FloatSize(1, 0));
#if ENABLE(CSS3_TEXT)
}
#endif // CSS3_TEXT
if (patWidth)
p->restore();
p->setRenderHint(QPainter::Antialiasing, antiAlias);
}
void GraphicsContext::drawEllipse(const IntRect& rect)
{
if (paintingDisabled())
return;
m_data->p()->drawEllipse(rect);
}
void GraphicsContext::drawConvexPolygon(size_t npoints, const FloatPoint* points, bool shouldAntialias)
{
if (paintingDisabled())
return;
if (npoints <= 1)
return;
QPolygonF polygon(npoints);
for (size_t i = 0; i < npoints; i++)
polygon[i] = points[i];
QPainter* p = m_data->p();
const bool antiAlias = p->testRenderHint(QPainter::Antialiasing);
p->setRenderHint(QPainter::Antialiasing, shouldAntialias);
p->drawConvexPolygon(polygon);
p->setRenderHint(QPainter::Antialiasing, antiAlias);
}
void GraphicsContext::clipConvexPolygon(size_t numPoints, const FloatPoint* points, bool antialiased)
{
if (paintingDisabled())
return;
if (numPoints <= 1)
return;
QPainterPath path(points[0]);
for (size_t i = 1; i < numPoints; ++i)
path.lineTo(points[i]);
path.setFillRule(Qt::WindingFill);
QPainter* p = m_data->p();
bool painterWasAntialiased = p->testRenderHint(QPainter::Antialiasing);
if (painterWasAntialiased != antialiased)
p->setRenderHint(QPainter::Antialiasing, antialiased);
p->setClipPath(path, Qt::IntersectClip);
if (painterWasAntialiased != antialiased)
p->setRenderHint(QPainter::Antialiasing, painterWasAntialiased);
}
void GraphicsContext::fillPath(const Path& path)
{
if (paintingDisabled())
return;
QPainter* p = m_data->p();
QPainterPath platformPath = path.platformPath();
platformPath.setFillRule(toQtFillRule(fillRule()));
if (hasShadow()) {
if (mustUseShadowBlur() || m_state.fillPattern || m_state.fillGradient)
{
ShadowBlur shadow(m_state);
GraphicsContext* shadowContext = shadow.beginShadowLayer(this, platformPath.controlPointRect());
if (shadowContext) {
QPainter* shadowPainter = shadowContext->platformContext();
if (m_state.fillPattern) {
shadowPainter->fillPath(platformPath, QBrush(m_state.fillPattern->createPlatformPattern()));
} else if (m_state.fillGradient) {
QBrush brush(*m_state.fillGradient->platformGradient());
brush.setTransform(m_state.fillGradient->gradientSpaceTransform());
shadowPainter->fillPath(platformPath, brush);
} else {
shadowPainter->fillPath(platformPath, p->brush());
}
shadow.endShadowLayer(this);
}
} else {
QPointF offset(m_state.shadowOffset.width(), m_state.shadowOffset.height());
p->translate(offset);
QColor shadowColor = m_state.shadowColor;
shadowColor.setAlphaF(shadowColor.alphaF() * p->brush().color().alphaF());
p->fillPath(platformPath, shadowColor);
p->translate(-offset);
}
}
if (m_state.fillPattern) {
p->fillPath(platformPath, QBrush(m_state.fillPattern->createPlatformPattern()));
} else if (m_state.fillGradient) {
QBrush brush(*m_state.fillGradient->platformGradient());
brush.setTransform(m_state.fillGradient->gradientSpaceTransform());
p->fillPath(platformPath, brush);
} else
p->fillPath(platformPath, p->brush());
}
inline static void fillPathStroke(QPainter* painter, QPainterPathStroker& pathStroker, const QPainterPath& platformPath, const QBrush& brush)
{
QPainterPath stroke = pathStroker.createStroke(platformPath);
painter->fillPath(stroke, brush);
}
void GraphicsContext::strokePath(const Path& path)
{
if (paintingDisabled())
return;
QPainter* p = m_data->p();
QPen pen(p->pen());
QPainterPath platformPath = path.platformPath();
platformPath.setFillRule(toQtFillRule(fillRule()));
QPainterPathStroker pathStroker;
pathStroker.setJoinStyle(pen.joinStyle());
pathStroker.setDashOffset(pen.dashOffset());
pathStroker.setMiterLimit(pen.miterLimit());
pathStroker.setCapStyle(pen.capStyle());
pathStroker.setWidth(pen.widthF());
if (hasShadow()) {
if (mustUseShadowBlur() || m_state.strokePattern || m_state.strokeGradient)
{
ShadowBlur shadow(m_state);
FloatRect boundingRect = platformPath.controlPointRect();
boundingRect.inflate(pen.miterLimit() + pen.widthF());
GraphicsContext* shadowContext = shadow.beginShadowLayer(this, boundingRect);
if (shadowContext) {
QPainter* shadowPainter = shadowContext->platformContext();
if (m_state.strokeGradient) {
QBrush brush(*m_state.strokeGradient->platformGradient());
brush.setTransform(m_state.strokeGradient->gradientSpaceTransform());
fillPathStroke(shadowPainter, pathStroker, platformPath, brush);
} else
fillPathStroke(shadowPainter, pathStroker, platformPath, pen.brush());
shadow.endShadowLayer(this);
}
} else {
QPointF offset(m_state.shadowOffset.width(), m_state.shadowOffset.height());
p->translate(offset);
QColor shadowColor = m_state.shadowColor;
shadowColor.setAlphaF(shadowColor.alphaF() * pen.color().alphaF());
QPen shadowPen(pen);
shadowPen.setColor(shadowColor);
fillPathStroke(p, pathStroker, platformPath, shadowPen.brush());
p->translate(-offset);
}
}
if (m_state.strokePattern) {
QBrush brush = m_state.strokePattern->createPlatformPattern();
fillPathStroke(p, pathStroker, platformPath, brush);
} else if (m_state.strokeGradient) {
QBrush brush(*m_state.strokeGradient->platformGradient());
brush.setTransform(m_state.strokeGradient->gradientSpaceTransform());
fillPathStroke(p, pathStroker, platformPath, brush);
} else
fillPathStroke(p, pathStroker, platformPath, pen.brush());
}
static inline void drawRepeatPattern(QPainter* p, PassRefPtr<Pattern> pattern, const FloatRect& rect)
{
ASSERT(pattern);
const QBrush brush = pattern->createPlatformPattern();
if (brush.style() != Qt::TexturePattern)
return;
const bool repeatX = pattern->repeatX();
const bool repeatY = pattern->repeatY();
QRectF targetRect(rect);
const int w = brush.texture().width();
const int h = brush.texture().height();
ASSERT(p);
QRegion oldClip;
if (p->hasClipping())
oldClip = p->clipRegion();
ASSERT(!brush.transform().isRotating());
QRectF clip = targetRect;
QRectF patternRect = brush.transform().mapRect(QRectF(0, 0, w, h));
if (!repeatX) {
clip.setLeft(patternRect.left());
clip.setWidth(patternRect.width());
}
if (!repeatY) {
clip.setTop(patternRect.top());
clip.setHeight(patternRect.height());
}
if (!repeatX || !repeatY)
p->setClipRect(clip);
p->fillRect(targetRect, brush);
if (!oldClip.isEmpty())
p->setClipRegion(oldClip);
else if (!repeatX || !repeatY)
p->setClipping(false);
}
void GraphicsContext::fillRect(const FloatRect& rect)
{
if (paintingDisabled())
return;
QPainter* p = m_data->p();
QRectF normalizedRect = rect.normalized();
if (m_state.fillPattern) {
if (hasShadow()) {
ShadowBlur shadow(m_state);
GraphicsContext* shadowContext = shadow.beginShadowLayer(this, normalizedRect);
if (shadowContext) {
QPainter* shadowPainter = shadowContext->platformContext();
drawRepeatPattern(shadowPainter, m_state.fillPattern, normalizedRect);
shadow.endShadowLayer(this);
}
}
drawRepeatPattern(p, m_state.fillPattern, normalizedRect);
} else if (m_state.fillGradient) {
QBrush brush(*m_state.fillGradient->platformGradient());
brush.setTransform(m_state.fillGradient->gradientSpaceTransform());
if (hasShadow()) {
ShadowBlur shadow(m_state);
GraphicsContext* shadowContext = shadow.beginShadowLayer(this, normalizedRect);
if (shadowContext) {
QPainter* shadowPainter = shadowContext->platformContext();
shadowPainter->fillRect(normalizedRect, brush);
shadow.endShadowLayer(this);
}
}
p->fillRect(normalizedRect, brush);
} else {
if (hasShadow()) {
if (mustUseShadowBlur()) {
ShadowBlur shadow(m_state);
if (!getCTM().preservesAxisAlignment()) {
GraphicsContext* shadowContext = shadow.beginShadowLayer(this, normalizedRect);
if (shadowContext) {
QPainter* shadowPainter = shadowContext->platformContext();
shadowPainter->fillRect(normalizedRect, p->brush());
shadow.endShadowLayer(this);
}
} else
shadow.drawRectShadow(this, rect, RoundedRect::Radii());
} else {
QColor shadowColor = m_state.shadowColor;
shadowColor.setAlphaF(shadowColor.alphaF() * p->brush().color().alphaF());
p->fillRect(normalizedRect.translated(QPointF(m_state.shadowOffset.width(), m_state.shadowOffset.height())), shadowColor);
}
}
p->fillRect(normalizedRect, p->brush());
}
}
void GraphicsContext::fillRect(const FloatRect& rect, const Color& color, ColorSpace colorSpace)
{
if (paintingDisabled() || !color.isValid())
return;
QRectF platformRect(rect);
QPainter* p = m_data->p();
if (hasShadow()) {
if (mustUseShadowBlur()) {
ShadowBlur shadow(m_state);
shadow.drawRectShadow(this, platformRect, RoundedRect::Radii());
} else {
QColor shadowColor = m_state.shadowColor;
shadowColor.setAlphaF(shadowColor.alphaF() * p->brush().color().alphaF());
p->fillRect(platformRect.translated(QPointF(m_state.shadowOffset.width(), m_state.shadowOffset.height())), shadowColor);
}
}
p->fillRect(platformRect, QColor(color));
}
void GraphicsContext::fillRoundedRect(const IntRect& rect, const IntSize& topLeft, const IntSize& topRight, const IntSize& bottomLeft, const IntSize& bottomRight, const Color& color, ColorSpace colorSpace)
{
if (paintingDisabled() || !color.isValid())
return;
Path path;
path.addRoundedRect(rect, topLeft, topRight, bottomLeft, bottomRight);
QPainter* p = m_data->p();
if (hasShadow()) {
if (mustUseShadowBlur()) {
ShadowBlur shadow(m_state);
shadow.drawRectShadow(this, rect, RoundedRect::Radii(topLeft, topRight, bottomLeft, bottomRight));
} else {
const QPointF shadowOffset(m_state.shadowOffset.width(), m_state.shadowOffset.height());
p->translate(shadowOffset);
p->fillPath(path.platformPath(), QColor(m_state.shadowColor));
p->translate(-shadowOffset);
}
}
p->fillPath(path.platformPath(), QColor(color));
}
void GraphicsContext::fillRectWithRoundedHole(const IntRect& rect, const RoundedRect& roundedHoleRect, const Color& color, ColorSpace colorSpace)
{
if (paintingDisabled() || !color.isValid())
return;
Path path;
path.addRect(rect);
if (!roundedHoleRect.radii().isZero())
path.addRoundedRect(roundedHoleRect);
else
path.addRect(roundedHoleRect.rect());
QPainterPath platformPath = path.platformPath();
platformPath.setFillRule(Qt::OddEvenFill);
QPainter* p = m_data->p();
if (hasShadow()) {
if (mustUseShadowBlur()) {
ShadowBlur shadow(m_state);
shadow.drawInsetShadow(this, rect, roundedHoleRect.rect(), roundedHoleRect.radii());
} else {
const QPointF shadowOffset(m_state.shadowOffset.width(), m_state.shadowOffset.height());
p->translate(shadowOffset);
p->fillPath(platformPath, QColor(m_state.shadowColor));
p->translate(-shadowOffset);
}
}
p->fillPath(platformPath, QColor(color));
}
bool GraphicsContext::isInTransparencyLayer() const
{
return m_data->layerCount;
}
void GraphicsContext::clip(const IntRect& rect)
{
if (paintingDisabled())
return;
m_data->p()->setClipRect(rect, Qt::IntersectClip);
}
void GraphicsContext::clip(const FloatRect& rect)
{
if (paintingDisabled())
return;
m_data->p()->setClipRect(rect, Qt::IntersectClip);
}
IntRect GraphicsContext::clipBounds() const
{
QPainter* p = m_data->p();
QRectF clipRect;
clipRect = p->transform().inverted().mapRect(p->window());
if (p->hasClipping())
clipRect = clipRect.intersected(m_data->clipBoundingRect());
return enclosingIntRect(clipRect);
}
void GraphicsContext::clipPath(const Path& path, WindRule clipRule)
{
if (paintingDisabled())
return;
QPainter* p = m_data->p();
QPainterPath platformPath = path.platformPath();
platformPath.setFillRule(clipRule == RULE_EVENODD ? Qt::OddEvenFill : Qt::WindingFill);
p->setClipPath(platformPath, Qt::IntersectClip);
}
void drawFocusRingForPath(QPainter* p, const QPainterPath& path, const Color& color, bool antiAliasing)
{
const bool antiAlias = p->testRenderHint(QPainter::Antialiasing);
p->setRenderHint(QPainter::Antialiasing, antiAliasing);
const QPen oldPen = p->pen();
const QBrush oldBrush = p->brush();
QPen nPen = p->pen();
nPen.setColor(color);
p->setBrush(Qt::NoBrush);
nPen.setStyle(Qt::DotLine);
p->strokePath(path, nPen);
p->setBrush(oldBrush);
p->setPen(oldPen);
p->setRenderHint(QPainter::Antialiasing, antiAlias);
}
void GraphicsContext::drawFocusRing(const Path& path, int , int offset, const Color& color)
{
if (paintingDisabled() || !color.isValid())
return;
drawFocusRingForPath(m_data->p(), path.platformPath(), color, m_data->antiAliasingForRectsAndLines);
}
void GraphicsContext::drawFocusRing(const Vector<IntRect>& rects, int width, int offset, const Color& color)
{
if (paintingDisabled() || !color.isValid())
return;
unsigned rectCount = rects.size();
if (!rects.size())
return;
int radius = (width - 1) / 2;
QPainterPath path;
for (unsigned i = 0; i < rectCount; ++i) {
QRect rect = QRect((rects[i])).adjusted(-offset - radius, -offset - radius, offset + radius, offset + radius);
QPainterPath tmpPath;
tmpPath.addRoundedRect(rect, radius, radius);
path = path.united(tmpPath);
}
drawFocusRingForPath(m_data->p(), path, color, m_data->antiAliasingForRectsAndLines);
}
void GraphicsContext::drawLineForText(const FloatPoint& origin, float width, bool)
{
if (paintingDisabled())
return;
FloatPoint startPoint = origin;
FloatPoint endPoint = origin + FloatSize(width, 0);
#if defined(Q_WS_X11)
QPainter* p = m_data->p();
if (p->paintEngine()->type() == QPaintEngine::X11) {
float strokeWidth = strokeThickness();
if (static_cast<int>(strokeWidth) % 2) {
startPoint.setY(startPoint.y() - 1);
endPoint.setY(endPoint.y() - 1);
}
}
#endif // defined(Q_WS_X11)
drawLine(roundedIntPoint(startPoint), roundedIntPoint(endPoint));
}
static void drawErrorUnderline(QPainter *painter, qreal x, qreal y, qreal width, qreal height)
{
const qreal heightSquares = 2.5;
qreal square = height / heightSquares;
qreal halfSquare = 0.5 * square;
qreal unitWidth = (heightSquares - 1.0) * square;
int widthUnits = static_cast<int>((width + 0.5 * unitWidth) / unitWidth);
x += 0.5 * (width - widthUnits * unitWidth);
width = widthUnits * unitWidth;
qreal bottom = y + height;
qreal top = y;
QPainterPath path;
path.moveTo(x - halfSquare, top + halfSquare);
int i = 0;
for (i = 0; i < widthUnits; i += 2) {
qreal middle = x + (i + 1) * unitWidth;
qreal right = x + (i + 2) * unitWidth;
path.lineTo(middle, bottom);
if (i + 2 == widthUnits)
path.lineTo(right + halfSquare, top + halfSquare); else if (i + 1 != widthUnits)
path.lineTo(right, top + square); }
for (i -= 2; i >= 0; i -= 2) {
qreal left = x + i * unitWidth;
qreal middle = x + (i + 1) * unitWidth;
qreal right = x + (i + 2) * unitWidth;
if (i + 1 == widthUnits)
path.lineTo(middle + halfSquare, bottom - halfSquare); else {
if (i + 2 == widthUnits)
path.lineTo(right, top);
path.lineTo(middle, bottom - halfSquare); }
path.lineTo(left, top); }
painter->drawPath(path);
}
void GraphicsContext::drawLineForDocumentMarker(const FloatPoint& origin, float width, DocumentMarkerLineStyle style)
{
if (paintingDisabled())
return;
QPainter* painter = platformContext();
const QPen originalPen = painter->pen();
switch (style) {
case DocumentMarkerSpellingLineStyle:
painter->setPen(Qt::red);
break;
case DocumentMarkerGrammarLineStyle:
painter->setPen(Qt::green);
break;
default:
return;
}
drawErrorUnderline(painter, origin.x(), origin.y(), width, cMisspellingLineThickness);
painter->setPen(originalPen);
}
FloatRect GraphicsContext::roundToDevicePixels(const FloatRect& frect, RoundingMode)
{
QPainter* painter = platformContext();
QTransform deviceTransform = painter->deviceTransform();
if (deviceTransform.isIdentity())
return frect;
qreal deviceScaleX = sqrtf(deviceTransform.m11() * deviceTransform.m11() + deviceTransform.m12() * deviceTransform.m12());
qreal deviceScaleY = sqrtf(deviceTransform.m21() * deviceTransform.m21() + deviceTransform.m22() * deviceTransform.m22());
QPoint deviceOrigin(frect.x() * deviceScaleX, frect.y() * deviceScaleY);
QPoint deviceLowerRight(frect.maxX() * deviceScaleX, frect.maxY() * deviceScaleY);
if (deviceOrigin.y() == deviceLowerRight.y() && frect.height())
deviceLowerRight.setY(deviceLowerRight.y() + 1);
if (deviceOrigin.x() == deviceLowerRight.x() && frect.width())
deviceLowerRight.setX(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);
}
void GraphicsContext::setPlatformShadow(const FloatSize& size, float blur, const Color& color, ColorSpace colorSpace)
{
if (m_state.shadowsIgnoreTransforms) {
m_state.shadowOffset = FloatSize(size.width(), -size.height());
}
}
void GraphicsContext::clearPlatformShadow()
{
}
void GraphicsContext::pushTransparencyLayerInternal(const QRect &rect, qreal opacity, QPixmap& alphaMask)
{
QPainter* p = m_data->p();
QTransform deviceTransform = p->transform();
QRect deviceClip = deviceTransform.mapRect(rect);
alphaMask = alphaMask.transformed(deviceTransform);
if (alphaMask.width() != deviceClip.width() || alphaMask.height() != deviceClip.height())
alphaMask = alphaMask.scaled(deviceClip.width(), deviceClip.height());
m_data->layers.push(new TransparencyLayer(p, deviceClip, 1.0, alphaMask));
}
void GraphicsContext::beginPlatformTransparencyLayer(float opacity)
{
if (paintingDisabled())
return;
int x, y, w, h;
x = y = 0;
QPainter* p = m_data->p();
const QPaintDevice* device = p->device();
w = device->width();
h = device->height();
if (p->hasClipping()) {
QRectF clip = m_data->clipBoundingRect();
QRectF deviceClip = p->transform().mapRect(clip);
x = int(qBound(qreal(0), deviceClip.x(), (qreal)w));
y = int(qBound(qreal(0), deviceClip.y(), (qreal)h));
w = int(qBound(qreal(0), deviceClip.width(), (qreal)w) + 2);
h = int(qBound(qreal(0), deviceClip.height(), (qreal)h) + 2);
}
QPixmap emptyAlphaMask;
m_data->layers.push(new TransparencyLayer(p, QRect(x, y, w, h), opacity, emptyAlphaMask));
++m_data->layerCount;
}
void GraphicsContext::endPlatformTransparencyLayer()
{
if (paintingDisabled())
return;
TransparencyLayer* layer = m_data->layers.pop();
if (!layer->alphaMask.isNull()) {
layer->painter.resetTransform();
layer->painter.setCompositionMode(QPainter::CompositionMode_DestinationIn);
layer->painter.drawPixmap(QPoint(), layer->alphaMask);
} else
--m_data->layerCount; layer->painter.end();
QPainter* p = m_data->p();
p->save();
p->resetTransform();
p->setOpacity(layer->opacity);
p->drawPixmap(layer->offset, layer->pixmap);
p->restore();
delete layer;
}
bool GraphicsContext::supportsTransparencyLayers()
{
return true;
}
void GraphicsContext::clearRect(const FloatRect& rect)
{
if (paintingDisabled())
return;
QPainter* p = m_data->p();
QPainter::CompositionMode currentCompositionMode = p->compositionMode();
p->setCompositionMode(QPainter::CompositionMode_Source);
p->fillRect(rect, Qt::transparent);
p->setCompositionMode(currentCompositionMode);
}
void GraphicsContext::strokeRect(const FloatRect& rect, float lineWidth)
{
if (paintingDisabled())
return;
Path path;
path.addRect(rect);
float previousStrokeThickness = strokeThickness();
if (lineWidth != previousStrokeThickness)
setStrokeThickness(lineWidth);
strokePath(path);
if (lineWidth != previousStrokeThickness)
setStrokeThickness(previousStrokeThickness);
}
void GraphicsContext::setLineCap(LineCap lc)
{
if (paintingDisabled())
return;
QPainter* p = m_data->p();
QPen nPen = p->pen();
nPen.setCapStyle(toQtLineCap(lc));
p->setPen(nPen);
}
void GraphicsContext::setLineDash(const DashArray& dashes, float dashOffset)
{
QPainter* p = m_data->p();
QPen pen = p->pen();
unsigned dashLength = dashes.size();
if (dashLength) {
QVector<qreal> pattern;
unsigned count = dashLength;
if (dashLength % 2)
count *= 2;
float penWidth = narrowPrecisionToFloat(double(pen.widthF()));
if (penWidth <= 0.f)
penWidth = 1.f;
for (unsigned i = 0; i < count; i++)
pattern.append(dashes[i % dashLength] / penWidth);
pen.setDashPattern(pattern);
pen.setDashOffset(dashOffset / penWidth);
} else
pen.setStyle(Qt::SolidLine);
p->setPen(pen);
}
void GraphicsContext::setLineJoin(LineJoin lj)
{
if (paintingDisabled())
return;
QPainter* p = m_data->p();
QPen nPen = p->pen();
nPen.setJoinStyle(toQtLineJoin(lj));
p->setPen(nPen);
}
void GraphicsContext::setMiterLimit(float limit)
{
if (paintingDisabled())
return;
QPainter* p = m_data->p();
QPen nPen = p->pen();
nPen.setMiterLimit(limit);
p->setPen(nPen);
}
void GraphicsContext::setAlpha(float opacity)
{
if (paintingDisabled())
return;
QPainter* p = m_data->p();
p->setOpacity(opacity);
}
void GraphicsContext::setPlatformCompositeOperation(CompositeOperator op, BlendMode)
{
if (paintingDisabled())
return;
m_data->p()->setCompositionMode(toQtCompositionMode(op));
}
void GraphicsContext::clip(const Path& path, WindRule windRule)
{
if (paintingDisabled())
return;
QPainterPath clipPath = path.platformPath();
clipPath.setFillRule(toQtFillRule(windRule));
m_data->p()->setClipPath(clipPath, Qt::IntersectClip);
}
void GraphicsContext::canvasClip(const Path& path, WindRule windRule)
{
clip(path, windRule);
}
void GraphicsContext::clipOut(const Path& path)
{
if (paintingDisabled())
return;
QPainter* p = m_data->p();
QPainterPath clippedOut = path.platformPath();
QPainterPath newClip;
newClip.setFillRule(Qt::OddEvenFill);
if (p->hasClipping()) {
newClip.addRect(m_data->clipBoundingRect());
newClip.addPath(clippedOut);
p->setClipPath(newClip, Qt::IntersectClip);
} else {
QRect windowRect = p->transform().inverted().mapRect(p->window());
newClip.addRect(windowRect);
newClip.addPath(clippedOut.intersected(newClip));
p->setClipPath(newClip);
}
}
void GraphicsContext::translate(float x, float y)
{
if (paintingDisabled())
return;
m_data->p()->translate(x, y);
}
void GraphicsContext::rotate(float radians)
{
if (paintingDisabled())
return;
QTransform rotation = QTransform().rotateRadians(radians);
m_data->p()->setTransform(rotation, true);
}
void GraphicsContext::scale(const FloatSize& s)
{
if (paintingDisabled())
return;
m_data->p()->scale(s.width(), s.height());
}
void GraphicsContext::clipOut(const IntRect& rect)
{
if (paintingDisabled())
return;
QPainter* p = m_data->p();
QPainterPath newClip;
newClip.setFillRule(Qt::OddEvenFill);
if (p->hasClipping()) {
newClip.addRect(m_data->clipBoundingRect());
newClip.addRect(QRect(rect));
p->setClipPath(newClip, Qt::IntersectClip);
} else {
QRect clipOutRect(rect);
QRect window = p->transform().inverted().mapRect(p->window());
clipOutRect &= window;
newClip.addRect(window);
newClip.addRect(clipOutRect);
p->setClipPath(newClip);
}
}
void GraphicsContext::concatCTM(const AffineTransform& transform)
{
if (paintingDisabled())
return;
m_data->p()->setWorldTransform(transform, true);
}
void GraphicsContext::setCTM(const AffineTransform& transform)
{
if (paintingDisabled())
return;
m_data->p()->setWorldTransform(transform);
}
#if ENABLE(3D_RENDERING)
TransformationMatrix GraphicsContext::get3DTransform() const
{
if (paintingDisabled())
return TransformationMatrix();
return platformContext()->combinedTransform();
}
void GraphicsContext::concat3DTransform(const TransformationMatrix& transform)
{
if (paintingDisabled())
return;
m_data->p()->setWorldTransform(transform, true);
}
void GraphicsContext::set3DTransform(const TransformationMatrix& transform)
{
if (paintingDisabled())
return;
m_data->p()->setWorldTransform(transform, false);
}
#endif
void GraphicsContext::setURLForRect(const KURL&, const IntRect&)
{
notImplemented();
}
void GraphicsContext::setPlatformStrokeColor(const Color& color, ColorSpace colorSpace)
{
if (paintingDisabled() || !color.isValid())
return;
QPainter* p = m_data->p();
QPen newPen(p->pen());
m_data->solidColor.setColor(color);
newPen.setBrush(m_data->solidColor);
p->setPen(newPen);
}
void GraphicsContext::setPlatformStrokeStyle(StrokeStyle strokeStyle)
{
if (paintingDisabled())
return;
QPainter* p = m_data->p();
QPen newPen(p->pen());
newPen.setStyle(toQPenStyle(strokeStyle));
p->setPen(newPen);
}
void GraphicsContext::setPlatformStrokeThickness(float thickness)
{
if (paintingDisabled())
return;
QPainter* p = m_data->p();
QPen newPen(p->pen());
newPen.setWidthF(thickness);
p->setPen(newPen);
}
void GraphicsContext::setPlatformFillColor(const Color& color, ColorSpace colorSpace)
{
if (paintingDisabled() || !color.isValid())
return;
m_data->solidColor.setColor(color);
m_data->p()->setBrush(m_data->solidColor);
}
void GraphicsContext::setPlatformShouldAntialias(bool enable)
{
if (paintingDisabled())
return;
m_data->p()->setRenderHint(QPainter::Antialiasing, enable);
}
#if OS(WINDOWS)
HDC GraphicsContext::getWindowsContext(const IntRect& dstRect, bool supportAlphaBlend, bool mayCreateBitmap)
{
Q_ASSERT(mayCreateBitmap);
if (dstRect.isEmpty())
return 0;
BITMAPINFO bitmapInfo;
bitmapInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bitmapInfo.bmiHeader.biWidth = dstRect.width();
bitmapInfo.bmiHeader.biHeight = dstRect.height();
bitmapInfo.bmiHeader.biPlanes = 1;
bitmapInfo.bmiHeader.biBitCount = 32;
bitmapInfo.bmiHeader.biCompression = BI_RGB;
bitmapInfo.bmiHeader.biSizeImage = 0;
bitmapInfo.bmiHeader.biXPelsPerMeter = 0;
bitmapInfo.bmiHeader.biYPelsPerMeter = 0;
bitmapInfo.bmiHeader.biClrUsed = 0;
bitmapInfo.bmiHeader.biClrImportant = 0;
void* pixels = 0;
HBITMAP bitmap = ::CreateDIBSection(0, &bitmapInfo, DIB_RGB_COLORS, &pixels, 0, 0);
if (!bitmap)
return 0;
HDC displayDC = ::GetDC(0);
HDC bitmapDC = ::CreateCompatibleDC(displayDC);
::ReleaseDC(0, displayDC);
::SelectObject(bitmapDC, bitmap);
if (supportAlphaBlend) {
BITMAP bmpInfo;
GetObject(bitmap, sizeof(bmpInfo), &bmpInfo);
int bufferSize = bmpInfo.bmWidthBytes * bmpInfo.bmHeight;
memset(bmpInfo.bmBits, 0, bufferSize);
}
#if !OS(WINCE)
SetGraphicsMode(bitmapDC, GM_ADVANCED);
XFORM xform;
xform.eM11 = 1.0f;
xform.eM12 = 0.0f;
xform.eM21 = 0.0f;
xform.eM22 = 1.0f;
xform.eDx = -dstRect.x();
xform.eDy = -dstRect.y();
::SetWorldTransform(bitmapDC, &xform);
#endif
return bitmapDC;
}
void GraphicsContext::releaseWindowsContext(HDC hdc, const IntRect& dstRect, bool supportAlphaBlend, bool mayCreateBitmap)
{
Q_ASSERT(mayCreateBitmap);
if (hdc) {
if (!dstRect.isEmpty()) {
HBITMAP bitmap = static_cast<HBITMAP>(GetCurrentObject(hdc, OBJ_BITMAP));
BITMAP info;
GetObject(bitmap, sizeof(info), &info);
ASSERT(info.bmBitsPixel == 32);
QPixmap pixmap = qt_pixmapFromWinHBITMAP(bitmap, supportAlphaBlend ? HBitmapPremultipliedAlpha : HBitmapNoAlpha);
m_data->p()->drawPixmap(dstRect, pixmap);
::DeleteObject(bitmap);
}
::DeleteDC(hdc);
}
}
#endif
void GraphicsContext::setImageInterpolationQuality(InterpolationQuality quality)
{
m_data->imageInterpolationQuality = quality;
switch (quality) {
case InterpolationNone:
case InterpolationLow:
m_data->p()->setRenderHint(QPainter::SmoothPixmapTransform, false);
break;
case InterpolationMedium:
case InterpolationHigh:
m_data->p()->setRenderHint(QPainter::SmoothPixmapTransform, true);
break;
case InterpolationDefault:
default:
m_data->p()->setRenderHint(QPainter::SmoothPixmapTransform, m_data->initialSmoothPixmapTransformHint);
break;
};
}
InterpolationQuality GraphicsContext::imageInterpolationQuality() const
{
return m_data->imageInterpolationQuality;
}
void GraphicsContext::takeOwnershipOfPlatformContext()
{
m_data->takeOwnershipOfPlatformContext();
}
}