1 | /* |
2 | * Copyright (C) 2017 Metrological Group B.V. |
3 | * Copyright (C) 2017 Igalia S.L. |
4 | * |
5 | * Redistribution and use in source and binary forms, with or without |
6 | * modification, are permitted provided that the following conditions |
7 | * are met: |
8 | * 1. Redistributions of source code must retain the above copyright |
9 | * notice, this list of conditions and the following disclaimer. |
10 | * 2. Redistributions in binary form must reproduce the above copyright |
11 | * notice, this list of conditions and the following disclaimer in the |
12 | * documentation and/or other materials provided with the distribution. |
13 | * |
14 | * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY |
15 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
16 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
17 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR |
18 | * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
19 | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
20 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
21 | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY |
22 | * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
23 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
24 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
25 | */ |
26 | |
27 | #include "config.h" |
28 | #include "GraphicsContextImplCairo.h" |
29 | |
30 | #if USE(CAIRO) |
31 | |
32 | #include "CairoOperations.h" |
33 | #include "FloatRoundedRect.h" |
34 | #include "Font.h" |
35 | #include "GlyphBuffer.h" |
36 | #include "GraphicsContextPlatformPrivateCairo.h" |
37 | #include "ImageBuffer.h" |
38 | #include "IntRect.h" |
39 | |
40 | |
41 | namespace WebCore { |
42 | |
43 | GraphicsContext::GraphicsContextImplFactory GraphicsContextImplCairo::createFactory(PlatformContextCairo& platformContext) |
44 | { |
45 | return GraphicsContext::GraphicsContextImplFactory( |
46 | [&platformContext](GraphicsContext& context) |
47 | { |
48 | return std::make_unique<GraphicsContextImplCairo>(context, platformContext); |
49 | }); |
50 | } |
51 | |
52 | GraphicsContext::GraphicsContextImplFactory GraphicsContextImplCairo::createFactory(cairo_t* cairoContext) |
53 | { |
54 | return GraphicsContext::GraphicsContextImplFactory( |
55 | [cairoContext](GraphicsContext& context) |
56 | { |
57 | return std::make_unique<GraphicsContextImplCairo>(context, cairoContext); |
58 | }); |
59 | } |
60 | |
61 | GraphicsContextImplCairo::GraphicsContextImplCairo(GraphicsContext& context, PlatformContextCairo& platformContext) |
62 | : GraphicsContextImpl(context, FloatRect { }, AffineTransform { }) |
63 | , m_platformContext(platformContext) |
64 | , m_private(std::make_unique<GraphicsContextPlatformPrivate>(m_platformContext)) |
65 | { |
66 | m_platformContext.setGraphicsContextPrivate(m_private.get()); |
67 | m_private->syncContext(m_platformContext.cr()); |
68 | } |
69 | |
70 | GraphicsContextImplCairo::GraphicsContextImplCairo(GraphicsContext& context, cairo_t* cairoContext) |
71 | : GraphicsContextImpl(context, FloatRect { }, AffineTransform { }) |
72 | , m_ownedPlatformContext(std::make_unique<PlatformContextCairo>(cairoContext)) |
73 | , m_platformContext(*m_ownedPlatformContext) |
74 | , m_private(std::make_unique<GraphicsContextPlatformPrivate>(m_platformContext)) |
75 | { |
76 | m_platformContext.setGraphicsContextPrivate(m_private.get()); |
77 | m_private->syncContext(m_platformContext.cr()); |
78 | } |
79 | |
80 | GraphicsContextImplCairo::~GraphicsContextImplCairo() |
81 | { |
82 | m_platformContext.setGraphicsContextPrivate(nullptr); |
83 | } |
84 | |
85 | bool GraphicsContextImplCairo::hasPlatformContext() const |
86 | { |
87 | return true; |
88 | } |
89 | |
90 | PlatformContextCairo* GraphicsContextImplCairo::platformContext() const |
91 | { |
92 | return &m_platformContext; |
93 | } |
94 | |
95 | void GraphicsContextImplCairo::updateState(const GraphicsContextState& state, GraphicsContextState::StateChangeFlags flags) |
96 | { |
97 | if (flags & GraphicsContextState::StrokeThicknessChange) |
98 | Cairo::State::setStrokeThickness(m_platformContext, state.strokeThickness); |
99 | |
100 | if (flags & GraphicsContextState::StrokeStyleChange) |
101 | Cairo::State::setStrokeStyle(m_platformContext, state.strokeStyle); |
102 | |
103 | if (flags & GraphicsContextState::ShadowChange) { |
104 | if (state.shadowsIgnoreTransforms) { |
105 | // Meaning that this graphics context is associated with a CanvasRenderingContext |
106 | // We flip the height since CG and HTML5 Canvas have opposite Y axis |
107 | auto& mutableState = const_cast<GraphicsContextState&>(graphicsContext().state()); |
108 | auto& shadowOffset = state.shadowOffset; |
109 | mutableState.shadowOffset = { shadowOffset.width(), -shadowOffset.height() }; |
110 | } |
111 | } |
112 | |
113 | if (flags & GraphicsContextState::CompositeOperationChange) |
114 | Cairo::State::setCompositeOperation(m_platformContext, state.compositeOperator, state.blendMode); |
115 | |
116 | if (flags & GraphicsContextState::ShouldAntialiasChange) |
117 | Cairo::State::setShouldAntialias(m_platformContext, state.shouldAntialias); |
118 | } |
119 | |
120 | void GraphicsContextImplCairo::clearShadow() |
121 | { |
122 | } |
123 | |
124 | void GraphicsContextImplCairo::setLineCap(LineCap lineCap) |
125 | { |
126 | Cairo::setLineCap(m_platformContext, lineCap); |
127 | } |
128 | |
129 | void GraphicsContextImplCairo::setLineDash(const DashArray& dashes, float dashOffset) |
130 | { |
131 | Cairo::setLineDash(m_platformContext, dashes, dashOffset); |
132 | } |
133 | |
134 | void GraphicsContextImplCairo::setLineJoin(LineJoin lineJoin) |
135 | { |
136 | Cairo::setLineJoin(m_platformContext, lineJoin); |
137 | } |
138 | |
139 | void GraphicsContextImplCairo::setMiterLimit(float miterLimit) |
140 | { |
141 | Cairo::setMiterLimit(m_platformContext, miterLimit); |
142 | } |
143 | |
144 | void GraphicsContextImplCairo::fillRect(const FloatRect& rect) |
145 | { |
146 | auto& state = graphicsContext().state(); |
147 | Cairo::fillRect(m_platformContext, rect, Cairo::FillSource(state), Cairo::ShadowState(state)); |
148 | } |
149 | |
150 | void GraphicsContextImplCairo::fillRect(const FloatRect& rect, const Color& color) |
151 | { |
152 | Cairo::fillRect(m_platformContext, rect, color, Cairo::ShadowState(graphicsContext().state())); |
153 | } |
154 | |
155 | void GraphicsContextImplCairo::fillRect(const FloatRect& rect, Gradient& gradient) |
156 | { |
157 | RefPtr<cairo_pattern_t> platformGradient = adoptRef(gradient.createPlatformGradient(1.0)); |
158 | if (!platformGradient) |
159 | return; |
160 | |
161 | Cairo::save(m_platformContext); |
162 | Cairo::fillRect(m_platformContext, rect, platformGradient.get()); |
163 | Cairo::restore(m_platformContext); |
164 | } |
165 | |
166 | void GraphicsContextImplCairo::fillRect(const FloatRect& rect, const Color& color, CompositeOperator compositeOperator, BlendMode blendMode) |
167 | { |
168 | auto& state = graphicsContext().state(); |
169 | CompositeOperator previousOperator = state.compositeOperator; |
170 | |
171 | Cairo::State::setCompositeOperation(m_platformContext, compositeOperator, blendMode); |
172 | Cairo::fillRect(m_platformContext, rect, color, Cairo::ShadowState(state)); |
173 | Cairo::State::setCompositeOperation(m_platformContext, previousOperator, BlendMode::Normal); |
174 | } |
175 | |
176 | void GraphicsContextImplCairo::fillRoundedRect(const FloatRoundedRect& rect, const Color& color, BlendMode blendMode) |
177 | { |
178 | auto& state = graphicsContext().state(); |
179 | |
180 | CompositeOperator previousOperator = state.compositeOperator; |
181 | Cairo::State::setCompositeOperation(m_platformContext, previousOperator, blendMode); |
182 | |
183 | Cairo::ShadowState shadowState(state); |
184 | if (rect.isRounded()) |
185 | Cairo::fillRoundedRect(m_platformContext, rect, color, shadowState); |
186 | else |
187 | Cairo::fillRect(m_platformContext, rect.rect(), color, shadowState); |
188 | |
189 | Cairo::State::setCompositeOperation(m_platformContext, previousOperator, BlendMode::Normal); |
190 | } |
191 | |
192 | void GraphicsContextImplCairo::fillRectWithRoundedHole(const FloatRect& rect, const FloatRoundedRect& roundedHoleRect, const Color&) |
193 | { |
194 | Cairo::fillRectWithRoundedHole(m_platformContext, rect, roundedHoleRect, { }, Cairo::ShadowState(graphicsContext().state())); |
195 | } |
196 | |
197 | void GraphicsContextImplCairo::fillPath(const Path& path) |
198 | { |
199 | auto& state = graphicsContext().state(); |
200 | Cairo::fillPath(m_platformContext, path, Cairo::FillSource(state), Cairo::ShadowState(state)); |
201 | } |
202 | |
203 | void GraphicsContextImplCairo::fillEllipse(const FloatRect& rect) |
204 | { |
205 | Path path; |
206 | path.addEllipse(rect); |
207 | fillPath(path); |
208 | } |
209 | |
210 | void GraphicsContextImplCairo::strokeRect(const FloatRect& rect, float lineWidth) |
211 | { |
212 | auto& state = graphicsContext().state(); |
213 | Cairo::strokeRect(m_platformContext, rect, lineWidth, Cairo::StrokeSource(state), Cairo::ShadowState(state)); |
214 | } |
215 | |
216 | void GraphicsContextImplCairo::strokePath(const Path& path) |
217 | { |
218 | auto& state = graphicsContext().state(); |
219 | Cairo::strokePath(m_platformContext, path, Cairo::StrokeSource(state), Cairo::ShadowState(state)); |
220 | } |
221 | |
222 | void GraphicsContextImplCairo::strokeEllipse(const FloatRect& rect) |
223 | { |
224 | Path path; |
225 | path.addEllipse(rect); |
226 | strokePath(path); |
227 | } |
228 | |
229 | void GraphicsContextImplCairo::clearRect(const FloatRect& rect) |
230 | { |
231 | Cairo::clearRect(m_platformContext, rect); |
232 | } |
233 | |
234 | void GraphicsContextImplCairo::drawGlyphs(const Font& font, const GlyphBuffer& glyphBuffer, unsigned from, unsigned numGlyphs, const FloatPoint& point, FontSmoothingMode fontSmoothing) |
235 | { |
236 | UNUSED_PARAM(fontSmoothing); |
237 | if (!font.platformData().size()) |
238 | return; |
239 | |
240 | auto xOffset = point.x(); |
241 | Vector<cairo_glyph_t> glyphs(numGlyphs); |
242 | { |
243 | ASSERT(from + numGlyphs <= glyphBuffer.size()); |
244 | auto* glyphsData = glyphBuffer.glyphs(from); |
245 | auto* advances = glyphBuffer.advances(from); |
246 | |
247 | auto yOffset = point.y(); |
248 | for (size_t i = 0; i < numGlyphs; ++i) { |
249 | glyphs[i] = { glyphsData[i], xOffset, yOffset }; |
250 | xOffset += advances[i].width(); |
251 | yOffset -= advances[i].height(); |
252 | } |
253 | } |
254 | |
255 | cairo_scaled_font_t* scaledFont = font.platformData().scaledFont(); |
256 | double syntheticBoldOffset = font.syntheticBoldOffset(); |
257 | |
258 | auto& state = graphicsContext().state(); |
259 | Cairo::drawGlyphs(m_platformContext, Cairo::FillSource(state), Cairo::StrokeSource(state), |
260 | Cairo::ShadowState(state), point, scaledFont, syntheticBoldOffset, glyphs, xOffset, |
261 | state.textDrawingMode, state.strokeThickness, state.shadowOffset, state.shadowColor); |
262 | } |
263 | |
264 | ImageDrawResult GraphicsContextImplCairo::drawImage(Image& image, const FloatRect& destination, const FloatRect& source, const ImagePaintingOptions& imagePaintingOptions) |
265 | { |
266 | return GraphicsContextImpl::drawImageImpl(graphicsContext(), image, destination, source, imagePaintingOptions); |
267 | } |
268 | |
269 | ImageDrawResult GraphicsContextImplCairo::drawTiledImage(Image& image, const FloatRect& destination, const FloatPoint& source, const FloatSize& tileSize, const FloatSize& spacing, const ImagePaintingOptions& imagePaintingOptions) |
270 | { |
271 | return GraphicsContextImpl::drawTiledImageImpl(graphicsContext(), image, destination, source, tileSize, spacing, imagePaintingOptions); |
272 | } |
273 | |
274 | ImageDrawResult GraphicsContextImplCairo::drawTiledImage(Image& image, const FloatRect& destination, const FloatRect& source, const FloatSize& tileScaleFactor, Image::TileRule hRule, Image::TileRule vRule, const ImagePaintingOptions& imagePaintingOptions) |
275 | { |
276 | return GraphicsContextImpl::drawTiledImageImpl(graphicsContext(), image, destination, source, tileScaleFactor, hRule, vRule, imagePaintingOptions); |
277 | } |
278 | |
279 | void GraphicsContextImplCairo::drawNativeImage(const NativeImagePtr& image, const FloatSize& imageSize, const FloatRect& destRect, const FloatRect& srcRect, CompositeOperator compositeOperator, BlendMode blendMode, ImageOrientation orientation) |
280 | { |
281 | UNUSED_PARAM(imageSize); |
282 | auto& state = graphicsContext().state(); |
283 | Cairo::drawNativeImage(m_platformContext, image.get(), destRect, srcRect, compositeOperator, blendMode, orientation, state.imageInterpolationQuality, state.alpha, Cairo::ShadowState(state)); |
284 | } |
285 | |
286 | void GraphicsContextImplCairo::drawPattern(Image& image, const FloatRect& destRect, const FloatRect& tileRect, const AffineTransform& patternTransform, const FloatPoint& phase, const FloatSize&, CompositeOperator compositeOperator, BlendMode blendMode) |
287 | { |
288 | if (auto surface = image.nativeImageForCurrentFrame()) |
289 | Cairo::drawPattern(m_platformContext, surface.get(), IntSize(image.size()), destRect, tileRect, patternTransform, phase, compositeOperator, blendMode); |
290 | } |
291 | |
292 | void GraphicsContextImplCairo::drawRect(const FloatRect& rect, float borderThickness) |
293 | { |
294 | auto& state = graphicsContext().state(); |
295 | Cairo::drawRect(m_platformContext, rect, borderThickness, state.fillColor, state.strokeStyle, state.strokeColor); |
296 | } |
297 | |
298 | void GraphicsContextImplCairo::drawLine(const FloatPoint& point1, const FloatPoint& point2) |
299 | { |
300 | auto& state = graphicsContext().state(); |
301 | Cairo::drawLine(m_platformContext, point1, point2, state.strokeStyle, state.strokeColor, state.strokeThickness, state.shouldAntialias); |
302 | } |
303 | |
304 | void GraphicsContextImplCairo::drawLinesForText(const FloatPoint& point, float thickness, const DashArray& widths, bool printing, bool doubleUnderlines) |
305 | { |
306 | auto& state = graphicsContext().state(); |
307 | Cairo::drawLinesForText(m_platformContext, point, thickness, widths, printing, doubleUnderlines, state.strokeColor); |
308 | } |
309 | |
310 | void GraphicsContextImplCairo::drawDotsForDocumentMarker(const FloatRect& rect, DocumentMarkerLineStyle style) |
311 | { |
312 | Cairo::drawDotsForDocumentMarker(m_platformContext, rect, style); |
313 | } |
314 | |
315 | void GraphicsContextImplCairo::drawEllipse(const FloatRect& rect) |
316 | { |
317 | auto& state = graphicsContext().state(); |
318 | Cairo::drawEllipse(*platformContext(), rect, state.fillColor, state.strokeStyle, state.strokeColor, state.strokeThickness); |
319 | } |
320 | |
321 | void GraphicsContextImplCairo::drawPath(const Path&) |
322 | { |
323 | } |
324 | |
325 | void GraphicsContextImplCairo::drawFocusRing(const Path& path, float width, float offset, const Color& color) |
326 | { |
327 | UNUSED_PARAM(offset); |
328 | Cairo::drawFocusRing(m_platformContext, path, width, color); |
329 | } |
330 | |
331 | void GraphicsContextImplCairo::drawFocusRing(const Vector<FloatRect>& rects, float width, float offset, const Color& color) |
332 | { |
333 | UNUSED_PARAM(offset); |
334 | Cairo::drawFocusRing(m_platformContext, rects, width, color); |
335 | } |
336 | |
337 | void GraphicsContextImplCairo::save() |
338 | { |
339 | Cairo::save(m_platformContext); |
340 | } |
341 | |
342 | void GraphicsContextImplCairo::restore() |
343 | { |
344 | Cairo::restore(m_platformContext); |
345 | } |
346 | |
347 | void GraphicsContextImplCairo::translate(float x, float y) |
348 | { |
349 | Cairo::translate(m_platformContext, x, y); |
350 | } |
351 | |
352 | void GraphicsContextImplCairo::rotate(float angleInRadians) |
353 | { |
354 | Cairo::rotate(m_platformContext, angleInRadians); |
355 | } |
356 | |
357 | void GraphicsContextImplCairo::scale(const FloatSize& size) |
358 | { |
359 | Cairo::scale(m_platformContext, size); |
360 | } |
361 | |
362 | void GraphicsContextImplCairo::concatCTM(const AffineTransform& transform) |
363 | { |
364 | Cairo::concatCTM(m_platformContext, transform); |
365 | } |
366 | |
367 | void GraphicsContextImplCairo::setCTM(const AffineTransform& transform) |
368 | { |
369 | Cairo::State::setCTM(m_platformContext, transform); |
370 | } |
371 | |
372 | AffineTransform GraphicsContextImplCairo::getCTM(GraphicsContext::IncludeDeviceScale) |
373 | { |
374 | return Cairo::State::getCTM(m_platformContext); |
375 | } |
376 | |
377 | void GraphicsContextImplCairo::beginTransparencyLayer(float opacity) |
378 | { |
379 | Cairo::beginTransparencyLayer(m_platformContext, opacity); |
380 | } |
381 | |
382 | void GraphicsContextImplCairo::endTransparencyLayer() |
383 | { |
384 | Cairo::endTransparencyLayer(m_platformContext); |
385 | } |
386 | |
387 | void GraphicsContextImplCairo::clip(const FloatRect& rect) |
388 | { |
389 | Cairo::clip(m_platformContext, rect); |
390 | } |
391 | |
392 | void GraphicsContextImplCairo::clipOut(const FloatRect& rect) |
393 | { |
394 | Cairo::clipOut(m_platformContext, rect); |
395 | } |
396 | |
397 | void GraphicsContextImplCairo::clipOut(const Path& path) |
398 | { |
399 | Cairo::clipOut(m_platformContext, path); |
400 | } |
401 | |
402 | void GraphicsContextImplCairo::clipPath(const Path& path, WindRule clipRule) |
403 | { |
404 | Cairo::clipPath(m_platformContext, path, clipRule); |
405 | } |
406 | |
407 | IntRect GraphicsContextImplCairo::clipBounds() |
408 | { |
409 | return Cairo::State::getClipBounds(m_platformContext); |
410 | } |
411 | |
412 | void GraphicsContextImplCairo::clipToImageBuffer(ImageBuffer& buffer, const FloatRect& destRect) |
413 | { |
414 | RefPtr<Image> image = buffer.copyImage(DontCopyBackingStore); |
415 | if (!image) |
416 | return; |
417 | |
418 | if (auto surface = image->nativeImageForCurrentFrame()) |
419 | Cairo::clipToImageBuffer(m_platformContext, surface.get(), destRect); |
420 | } |
421 | |
422 | void GraphicsContextImplCairo::applyDeviceScaleFactor(float) |
423 | { |
424 | } |
425 | |
426 | FloatRect GraphicsContextImplCairo::roundToDevicePixels(const FloatRect& rect, GraphicsContext::RoundingMode) |
427 | { |
428 | return Cairo::State::roundToDevicePixels(m_platformContext, rect); |
429 | } |
430 | |
431 | } // namespace WebCore |
432 | |
433 | #endif // USE(CAIRO) |
434 | |