📄 graphicscontextcairo.cpp
字号:
/* * Copyright (C) 2006 Apple Computer, Inc. All rights reserved. * Copyright (C) 2007 Alp Toker <alp@atoker.com> * Copyright (C) 2008 Dirk Schulze <vbs85@gmx.de> * Copyright (C) 2008 Nuanti Ltd. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */#include "config.h"#include "GraphicsContext.h"#if PLATFORM(CAIRO)#include "TransformationMatrix.h"#include "CairoPath.h"#include "FloatRect.h"#include "Font.h"#include "ImageBuffer.h"#include "IntRect.h"#include "NotImplemented.h"#include "Path.h"#include "Pattern.h"#include "SimpleFontData.h"#include <cairo.h>#include <math.h>#include <stdio.h>#include <wtf/MathExtras.h>#if PLATFORM(GTK)#include <gdk/gdk.h>#include <pango/pango.h>#elif PLATFORM(WIN)#include <cairo-win32.h>#endif#include "GraphicsContextPrivate.h"#include "GraphicsContextPlatformPrivateCairo.h"#ifndef M_PI#define M_PI 3.14159265358979323846#endifnamespace WebCore {static const unsigned aquaFocusRingColor = 0xFF7DADD9;Color focusRingColor(){ static Color focusRingColor = aquaFocusRingColor; return focusRingColor;}static inline void setColor(cairo_t* cr, const Color& col){ float red, green, blue, alpha; col.getRGBA(red, green, blue, alpha); cairo_set_source_rgba(cr, red, green, blue, alpha);}// A fillRect helperstatic inline void fillRectSourceOver(cairo_t* cr, const FloatRect& rect, const Color& col){ setColor(cr, col); cairo_rectangle(cr, rect.x(), rect.y(), rect.width(), rect.height()); cairo_set_operator(cr, CAIRO_OPERATOR_OVER); cairo_fill(cr);}GraphicsContext::GraphicsContext(PlatformGraphicsContext* cr) : m_common(createGraphicsContextPrivate()) , m_data(new GraphicsContextPlatformPrivate){ m_data->cr = cairo_reference(cr); setPaintingDisabled(!cr);}GraphicsContext::~GraphicsContext(){ destroyGraphicsContextPrivate(m_common); delete m_data;}TransformationMatrix GraphicsContext::getCTM() const{ cairo_t* cr = platformContext(); cairo_matrix_t m; cairo_get_matrix(cr, &m); return TransformationMatrix(m.xx, m.yx, m.xy, m.yy, m.x0, m.y0);}cairo_t* GraphicsContext::platformContext() const{ return m_data->cr;}void GraphicsContext::savePlatformState(){ cairo_save(m_data->cr); m_data->save();}void GraphicsContext::restorePlatformState(){ cairo_restore(m_data->cr); m_data->restore();}// Draws a filled rectangle with a stroked border.void GraphicsContext::drawRect(const IntRect& rect){ if (paintingDisabled()) return; cairo_t* cr = m_data->cr; cairo_save(cr); if (fillColor().alpha()) fillRectSourceOver(cr, rect, fillColor()); if (strokeStyle() != NoStroke) { setColor(cr, strokeColor()); FloatRect r(rect); r.inflate(-.5f); cairo_rectangle(cr, r.x(), r.y(), r.width(), r.height()); cairo_set_line_width(cr, 1.0); cairo_stroke(cr); } cairo_restore(cr);}// FIXME: Now that this is refactored, it should be shared by all contexts.static void adjustLineToPixelBoundaries(FloatPoint& p1, FloatPoint& p2, float strokeWidth, StrokeStyle style){ // For odd widths, we add in 0.5 to the appropriate x/y so that the float arithmetic // works out. For example, with a border width of 3, KHTML will pass us (y1+y2)/2, e.g., // (50+53)/2 = 103/2 = 51 when we want 51.5. It is always true that an even width gave // us a perfect position, but an odd width gave us a position that is off by exactly 0.5. if (style == DottedStroke || style == DashedStroke) { if (p1.x() == p2.x()) { p1.setY(p1.y() + strokeWidth); p2.setY(p2.y() - strokeWidth); } else { p1.setX(p1.x() + strokeWidth); p2.setX(p2.x() - strokeWidth); } } if (static_cast<int>(strokeWidth) % 2) { if (p1.x() == p2.x()) { // We're a vertical line. Adjust our x. p1.setX(p1.x() + 0.5); p2.setX(p2.x() + 0.5); } else { // We're a horizontal line. Adjust our y. p1.setY(p1.y() + 0.5); p2.setY(p2.y() + 0.5); } }}// This is only used to draw borders.void GraphicsContext::drawLine(const IntPoint& point1, const IntPoint& point2){ if (paintingDisabled()) return; StrokeStyle style = strokeStyle(); if (style == NoStroke) return; cairo_t* cr = m_data->cr; cairo_save(cr); float width = strokeThickness(); if (width < 1) width = 1; FloatPoint p1 = point1; FloatPoint p2 = point2; bool isVerticalLine = (p1.x() == p2.x()); adjustLineToPixelBoundaries(p1, p2, width, style); cairo_set_line_width(cr, width); int patWidth = 0; switch (style) { case NoStroke: case SolidStroke: break; case DottedStroke: patWidth = static_cast<int>(width); break; case DashedStroke: patWidth = 3*static_cast<int>(width); break; } setColor(cr, strokeColor()); cairo_set_antialias(cr, CAIRO_ANTIALIAS_NONE); if (patWidth) { // Do a rect fill of our endpoints. This ensures we always have the // appearance of being a border. We then draw the actual dotted/dashed line. if (isVerticalLine) { fillRectSourceOver(cr, FloatRect(p1.x() - width/2, p1.y() - width, width, width), strokeColor()); fillRectSourceOver(cr, FloatRect(p2.x() - width/2, p2.y(), width, width), strokeColor()); } else { fillRectSourceOver(cr, FloatRect(p1.x() - width, p1.y() - width/2, width, width), strokeColor()); fillRectSourceOver(cr, FloatRect(p2.x(), p2.y() - width/2, width, width), strokeColor()); } // Example: 80 pixels with a width of 30 pixels. // Remainder is 20. The maximum pixels of line we could paint // will be 50 pixels. int distance = (isVerticalLine ? (point2.y() - point1.y()) : (point2.x() - point1.x())) - 2*static_cast<int>(width); int remainder = distance%patWidth; int coverage = distance-remainder; int numSegments = coverage/patWidth; float patternOffset = 0; // Special case 1px dotted borders for speed. 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 (!evenNumberOfSegments) { if (remainder) patternOffset = (patWidth - remainder)/2; } } double dash = patWidth; cairo_set_dash(cr, &dash, 1, patternOffset); } cairo_move_to(cr, p1.x(), p1.y()); cairo_line_to(cr, p2.x(), p2.y()); cairo_stroke(cr); cairo_restore(cr);}// This method is only used to draw the little circles used in lists.void GraphicsContext::drawEllipse(const IntRect& rect){ if (paintingDisabled()) return; cairo_t* cr = m_data->cr; cairo_save(cr); float yRadius = .5 * rect.height(); float xRadius = .5 * rect.width(); cairo_translate(cr, rect.x() + xRadius, rect.y() + yRadius); cairo_scale(cr, xRadius, yRadius); cairo_arc(cr, 0., 0., 1., 0., 2 * M_PI); cairo_restore(cr); if (fillColor().alpha()) { setColor(cr, fillColor()); cairo_fill_preserve(cr); } if (strokeStyle() != NoStroke) { setColor(cr, strokeColor()); cairo_set_line_width(cr, strokeThickness()); cairo_stroke(cr); } cairo_new_path(cr);}void GraphicsContext::strokeArc(const IntRect& rect, int startAngle, int angleSpan){ if (paintingDisabled() || strokeStyle() == NoStroke) return; int x = rect.x(); int y = rect.y(); float w = rect.width(); float h = rect.height(); float scaleFactor = h / w; float reverseScaleFactor = w / h; float hRadius = w / 2; float vRadius = h / 2; float fa = startAngle; float falen = fa + angleSpan; cairo_t* cr = m_data->cr; cairo_save(cr); if (w != h) cairo_scale(cr, 1., scaleFactor); cairo_arc_negative(cr, x + hRadius, (y + vRadius) * reverseScaleFactor, hRadius, -fa * M_PI/180, -falen * M_PI/180); if (w != h) cairo_scale(cr, 1., reverseScaleFactor); float width = strokeThickness(); int patWidth = 0; switch (strokeStyle()) { case DottedStroke: patWidth = static_cast<int>(width / 2); break; case DashedStroke: patWidth = 3 * static_cast<int>(width / 2); break; default: break; } setColor(cr, strokeColor()); if (patWidth) { // Example: 80 pixels with a width of 30 pixels. // Remainder is 20. The maximum pixels of line we could paint // will be 50 pixels. int distance; if (hRadius == vRadius) distance = static_cast<int>((M_PI * hRadius) / 2.0); else // We are elliptical and will have to estimate the distance distance = static_cast<int>((M_PI * sqrtf((hRadius * hRadius + vRadius * vRadius) / 2.0)) / 2.0); int remainder = distance % patWidth; int coverage = distance - remainder; int numSegments = coverage / patWidth; float patternOffset = 0.0; // Special case 1px dotted borders for speed. 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.0; } else patternOffset = patWidth / 2.0; } else { if (remainder) patternOffset = (patWidth - remainder) / 2.0; } } double dash = patWidth; cairo_set_dash(cr, &dash, 1, patternOffset); } cairo_stroke(cr); cairo_restore(cr);}void GraphicsContext::drawConvexPolygon(size_t npoints, const FloatPoint* points, bool shouldAntialias){ if (paintingDisabled()) return; if (npoints <= 1) return; cairo_t* cr = m_data->cr; cairo_save(cr); cairo_set_antialias(cr, shouldAntialias ? CAIRO_ANTIALIAS_DEFAULT : CAIRO_ANTIALIAS_NONE); cairo_move_to(cr, points[0].x(), points[0].y()); for (size_t i = 1; i < npoints; i++) cairo_line_to(cr, points[i].x(), points[i].y()); cairo_close_path(cr); if (fillColor().alpha()) { setColor(cr, fillColor()); cairo_set_fill_rule(cr, CAIRO_FILL_RULE_EVEN_ODD); cairo_fill_preserve(cr); } if (strokeStyle() != NoStroke) { setColor(cr, strokeColor()); cairo_set_line_width(cr, strokeThickness()); cairo_stroke(cr); } cairo_new_path(cr); cairo_restore(cr);}void GraphicsContext::fillPath(){ if (paintingDisabled()) return; cairo_t* cr = m_data->cr; cairo_save(cr); cairo_set_fill_rule(cr, fillRule() == RULE_EVENODD ? CAIRO_FILL_RULE_EVEN_ODD : CAIRO_FILL_RULE_WINDING); switch (m_common->state.fillColorSpace) { case SolidColorSpace: setColor(cr, fillColor()); cairo_clip(cr); cairo_paint_with_alpha(cr, m_common->state.globalAlpha); break; case PatternColorSpace: { TransformationMatrix affine; cairo_set_source(cr, m_common->state.fillPattern->createPlatformPattern(affine)); cairo_clip(cr); cairo_paint_with_alpha(cr, m_common->state.globalAlpha); break; } case GradientColorSpace: cairo_pattern_t* pattern = m_common->state.fillGradient->platformGradient(); cairo_set_source(cr, pattern); cairo_clip(cr); cairo_paint_with_alpha(cr, m_common->state.globalAlpha); break; } cairo_restore(cr);}void GraphicsContext::strokePath(){ if (paintingDisabled()) return; cairo_t* cr = m_data->cr; cairo_save(cr); switch (m_common->state.strokeColorSpace) { case SolidColorSpace: float red, green, blue, alpha; strokeColor().getRGBA(red, green, blue, alpha); if (m_common->state.globalAlpha < 1.0f) alpha *= m_common->state.globalAlpha; cairo_set_source_rgba(cr, red, green, blue, alpha); cairo_stroke(cr); break; case PatternColorSpace: { TransformationMatrix affine; cairo_set_source(cr, m_common->state.strokePattern->createPlatformPattern(affine)); if (m_common->state.globalAlpha < 1.0f) { cairo_push_group(cr); cairo_paint_with_alpha(cr, m_common->state.globalAlpha); cairo_pop_group_to_source(cr); } cairo_stroke(cr); break; } case GradientColorSpace: cairo_pattern_t* pattern = m_common->state.strokeGradient->platformGradient(); cairo_set_source(cr, pattern); if (m_common->state.globalAlpha < 1.0f) { cairo_push_group(cr); cairo_paint_with_alpha(cr, m_common->state.globalAlpha); cairo_pop_group_to_source(cr); } cairo_stroke(cr); break; } cairo_restore(cr);}void GraphicsContext::drawPath(){ fillPath(); strokePath();}void GraphicsContext::fillRect(const FloatRect& rect){ if (paintingDisabled()) return; cairo_t* cr = m_data->cr; cairo_rectangle(cr, rect.x(), rect.y(), rect.width(), rect.height()); fillPath();}void GraphicsContext::fillRect(const FloatRect& rect, const Color& color){ if (paintingDisabled()) return; if (color.alpha()) fillRectSourceOver(m_data->cr, rect, color);}void GraphicsContext::clip(const FloatRect& rect){ if (paintingDisabled()) return; cairo_t* cr = m_data->cr; cairo_rectangle(cr, rect.x(), rect.y(), rect.width(), rect.height()); cairo_fill_rule_t savedFillRule = cairo_get_fill_rule(cr); cairo_set_fill_rule(cr, CAIRO_FILL_RULE_WINDING); cairo_clip(cr); cairo_set_fill_rule(cr, savedFillRule); m_data->clip(rect);}void GraphicsContext::clipPath(WindRule clipRule){ if (paintingDisabled()) return; cairo_t* cr = m_data->cr; cairo_set_fill_rule(cr, clipRule == RULE_EVENODD ? CAIRO_FILL_RULE_EVEN_ODD : CAIRO_FILL_RULE_WINDING); cairo_clip(cr);}void GraphicsContext::drawFocusRing(const Color& color){ if (paintingDisabled()) return;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -