Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(53)

Side by Side Diff: Source/core/paint/BoxBorderPainter.cpp

Issue 1156663008: Anti-aliased box borders (overdraw) Base URL: svn://svn.chromium.org/blink/trunk
Patch Set: split impl, RTEF Created 5 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « Source/core/paint/BoxBorderPainter.h ('k') | Source/core/paint/BoxPainter.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "config.h"
6 #include "core/paint/BoxBorderPainter.h"
7
8 #include "core/paint/BoxPainter.h"
9 #include "core/style/ComputedStyleConstants.h"
10 #include "platform/RuntimeEnabledFeatures.h"
11 #include "platform/graphics/GraphicsContext.h"
12 #include "platform/graphics/GraphicsContextStateSaver.h"
13
14 namespace blink {
15
16 namespace {
17
18 static_assert(BSTop == 0, "unexpected BoxSide enum value");
19 static_assert(BSRight == 1, "unexpected BoxSide enum value");
20 static_assert(BSBottom == 2, "unexpected BoxSide enum value");
21 static_assert(BSLeft == 3, "unexpected BoxSide enum value");
22
23 const BoxSide kAdjacentSidesCW[4] = { BSRight, BSBottom, BSLeft, BSTop };
24 const BoxSide kAdjacentSidesCCW[4] = { BSLeft, BSTop, BSRight, BSBottom };
25 const float kEdgeShiftMap[4][4] = {
26 { 0, 0, -1, 0},
27 { 0, -1, 0, 1 },
28 { 1, 0, -1, 0 },
29 { 0, -1, 0, 0 } };
30
31 inline BorderEdgeFlag edgeFlagForSide(BoxSide side)
32 {
33 return static_cast<BorderEdgeFlag>(1 << side);
34 }
35
36 inline bool includesEdge(BorderEdgeFlags flags, BoxSide side)
37 {
38 return flags & edgeFlagForSide(side);
39 }
40
41 inline bool includesAdjacentEdges(BorderEdgeFlags flags)
42 {
43 return (flags & (TopBorderEdge | BottomBorderEdge))
44 && (flags & (LeftBorderEdge | RightBorderEdge));
45 }
46
47 bool borderStyleHasInnerDetail(EBorderStyle style)
48 {
49 return style == GROOVE || style == RIDGE || style == DOUBLE;
50 }
51
52 // OUTSET darkens the bottom and right (and maybe lightens the top and left)
53 // INSET darkens the top and left (and maybe lightens the bottom and right)
54 inline bool borderStyleHasUnmatchedColorsAtCorner(EBorderStyle style, BoxSide si de, BoxSide adjacentSide)
55 {
56 // These styles match at the top/left and bottom/right.
57 if (style == INSET || style == GROOVE || style == RIDGE || style == OUTSET) {
58 const BorderEdgeFlags topRightFlags = edgeFlagForSide(BSTop) | edgeFlagF orSide(BSRight);
59 const BorderEdgeFlags bottomLeftFlags = edgeFlagForSide(BSBottom) | edge FlagForSide(BSLeft);
60
61 BorderEdgeFlags flags = edgeFlagForSide(side) | edgeFlagForSide(adjacent Side);
62 return flags == topRightFlags || flags == bottomLeftFlags;
63 }
64 return false;
65 }
66
67 bool borderStyleFillsBorderArea(EBorderStyle style)
68 {
69 return !(style == DOTTED || style == DASHED || style == DOUBLE);
70 }
71
72 inline bool borderStyleIsDottedOrDashed(EBorderStyle style)
73 {
74 return style == DOTTED || style == DASHED;
75 }
76
77 inline bool borderStylesRequireMitre(BoxSide side, BoxSide adjacentSide, EBorder Style style, EBorderStyle adjacentStyle)
78 {
79 if (style == DOUBLE || adjacentStyle == DOUBLE || adjacentStyle == GROOVE || adjacentStyle == RIDGE)
80 return true;
81
82 if (borderStyleIsDottedOrDashed(style) != borderStyleIsDottedOrDashed(adjace ntStyle))
83 return true;
84
85 if (style != adjacentStyle)
86 return true;
87
88 return borderStyleHasUnmatchedColorsAtCorner(style, side, adjacentSide);
89 }
90
91 inline bool colorsMatchAtCorner(BoxSide side, BoxSide adjacentSide, const Border Edge edges[])
92 {
93 if (edges[side].shouldRender() != edges[adjacentSide].shouldRender())
94 return false;
95
96 if (!edges[side].sharesColorWith(edges[adjacentSide]))
97 return false;
98
99 return !borderStyleHasUnmatchedColorsAtCorner(edges[side].borderStyle(), sid e, adjacentSide);
100 }
101
102 inline bool styleRequiresClipPolygon(EBorderStyle style)
103 {
104 // These are drawn with a stroke, so we have to clip to get corner miters.
105 return style == DOTTED || style == DASHED;
106 }
107
108 } // anonymous namespace
109
110 BoxBorderInfo::BoxBorderInfo(const ComputedStyle& style, BackgroundBleedAvoidanc e bleedAvoidance,
111 bool includeLogicalLeftEdge, bool includeLogicalRightEdge)
112 : style(style)
113 , bleedAvoidance(bleedAvoidance)
114 , includeLogicalLeftEdge(includeLogicalLeftEdge)
115 , includeLogicalRightEdge(includeLogicalRightEdge)
116 , visibleEdgeCount(0)
117 , firstVisibleEdge(0)
118 , visibleEdgeSet(0)
119 , isUniformStyle(true)
120 , isUniformWidth(true)
121 , isUniformColor(true)
122 , hasAlpha(false)
123 {
124 style.getBorderEdgeInfo(edges, includeLogicalLeftEdge, includeLogicalRightEd ge);
125
126 for (unsigned i = 0; i < WTF_ARRAY_LENGTH(edges); ++i) {
127 const BorderEdge& edge = edges[i];
128
129 if (!edge.shouldRender()) {
130 if (edge.presentButInvisible()) {
131 isUniformWidth = false;
132 isUniformColor = false;
133 }
134
135 continue;
136 }
137
138 visibleEdgeCount++;
139 visibleEdgeSet |= edgeFlagForSide(static_cast<BoxSide>(i));
140
141 hasAlpha = hasAlpha || edge.color.hasAlpha();
142
143 if (visibleEdgeCount == 1) {
144 firstVisibleEdge = i;
145 continue;
146 }
147
148 isUniformStyle = isUniformStyle && (edge.borderStyle() == edges[firstVis ibleEdge].borderStyle());
149 isUniformWidth = isUniformWidth && (edge.width == edges[firstVisibleEdge ].width);
150 isUniformColor = isUniformColor && (edge.color == edges[firstVisibleEdge ].color);
151 }
152 }
153
154 BoxBorderPainter::BoxBorderPainter(const BoxBorderInfo& borderInfo,
155 const FloatRoundedRect& outer, const FloatRoundedRect& inner)
156 : m_borderInfo(borderInfo)
157 , m_outer(outer)
158 , m_inner(inner)
159 {
160 BorderEdgeFlags edgeSet = borderInfo.visibleEdgeSet;
161 ASSERT(edgeSet);
162
163 while (edgeSet) {
164 m_opacityGroupIndices.append(m_opacityGroupIndices.size());
165 m_opacityGroups.append(OpacityGroup());
166 OpacityGroup& currentGroup = m_opacityGroups.last();
167 BorderEdgeFlags currentSet = 0;
168
169 for (unsigned i = borderInfo.firstVisibleEdge; i < 4; ++i) {
170 BoxSide side = static_cast<BoxSide>(i);
171 if (!includesEdge(edgeSet, side))
172 continue;
173
174 unsigned sideAlpha = borderInfo.edges[side].color.alpha();
175 if (currentGroup.sides.isEmpty())
176 currentGroup.alpha = sideAlpha;
177
178 if (sideAlpha == currentGroup.alpha) {
179 currentGroup.sides.append(side);
180 currentGroup.edgeFlags |= edgeFlagForSide(side);
181 currentSet |= edgeFlagForSide(side);
182 }
183 }
184 ASSERT(currentSet);
185 ASSERT(!currentGroup.sides.isEmpty());
186
187 static const unsigned kStyleOrder[] = {
188 0 /* BNONE */,
189 0 /* BHIDDEN */,
190 2 /* INSET */,
191 2 /* GROOVE */,
192 2 /* OUTSET */,
193 2 /* RIDGE */,
194 1 /* DOTTED */,
195 1 /* DASHED */,
196 3 /* SOLID */,
197 2 /* DOUBLE */
198 };
199 std::sort(currentGroup.sides.begin(), currentGroup.sides.end(),
200 [&borderInfo] (BoxSide a, BoxSide b) -> bool
201 {
202 EBorderStyle style1 = borderInfo.edges[a].borderStyle();
203 EBorderStyle style2 = borderInfo.edges[b].borderStyle();
204 ASSERT(style1 != BNONE && style1 != BHIDDEN);
205 return kStyleOrder[style1] < kStyleOrder[style2];
206 });
207
208 edgeSet &= ~currentSet;
209 }
210
211 ASSERT(!m_opacityGroups.isEmpty());
212 ASSERT(m_opacityGroups.size() == m_opacityGroupIndices.size());
213
214 std::sort(m_opacityGroupIndices.begin(), m_opacityGroupIndices.end(),
215 [this] (size_t a, size_t b)
216 {
217 ASSERT(a < m_opacityGroups.size());
218 ASSERT(b < m_opacityGroups.size());
219 ASSERT(m_opacityGroups[a].alpha != m_opacityGroups[b].alpha);
220 return m_opacityGroups[a].alpha > m_opacityGroups[b].alpha;
221 });
222
223 if (outer.isRounded()) {
224 m_roundedPath.addRoundedRect(outer);
225 m_hasRadii[0] = !inner.radii().topLeft().isZero();
226 m_hasRadii[1] = !inner.radii().topRight().isZero();
227 m_hasRadii[2] = !inner.radii().bottomRight().isZero();
228 m_hasRadii[3] = !inner.radii().bottomLeft().isZero();
229 }
230 }
231
232 void BoxBorderPainter::paint(GraphicsContext* context) const
233 {
234 paintGroup(context, 0, 1);
235 }
236
237 BorderEdgeFlags BoxBorderPainter::paintGroup(GraphicsContext* context, unsigned index = 0,
238 float opacity = 1) const
239 {
240 ASSERT(opacity > 0 && opacity <= 1);
241
242 if (index >= m_opacityGroups.size())
243 return ~m_borderInfo.visibleEdgeSet;
244
245 const OpacityGroup& group = m_opacityGroups[m_opacityGroupIndices[index]];
246 const float groupOpacity = static_cast<float>(group.alpha) / 255;
247 ASSERT(groupOpacity <= opacity);
248
249 unsigned paintAlpha = group.alpha / opacity;
250 ASSERT(paintAlpha <= 255);
251
252 bool useLayer = group.alpha != 255
253 && (includesAdjacentEdges(group.edgeFlags) || (index + 1 < m_opacityGrou ps.size()));
254
255 if (useLayer) {
256 ASSERT(groupOpacity < opacity);
257
258 context->beginLayer(groupOpacity / opacity);
259 opacity *= groupOpacity;
260 paintAlpha = 255;
261 }
262
263 BorderEdgeFlags paintedSides = paintGroup(context, index + 1, opacity);
264
265 for (BoxSide side : group.sides) {
266 paintSide(context, side, paintAlpha, paintedSides);
267 paintedSides |= edgeFlagForSide(side);
268 }
269
270 if (useLayer)
271 context->endLayer();
272
273 return paintedSides;
274 }
275
276 void BoxBorderPainter::paintSide(GraphicsContext* context, BoxSide side, unsigne d alpha,
277 BorderEdgeFlags paintedSides) const
278 {
279 BoxSide adjacentSideCW = kAdjacentSidesCW[side];
280 BoxSide adjacentSideCCW = kAdjacentSidesCCW[side];
281
282 // FIXME: ugh
283 if (side == BSBottom || side == BSLeft)
284 std::swap(adjacentSideCW, adjacentSideCCW);
285
286 const BorderEdge& edge = m_borderInfo.edges[side];
287 const BorderEdge& adjacentEdgeCW = m_borderInfo.edges[adjacentSideCW];
288 const BorderEdge& adjacentEdgeCCW = m_borderInfo.edges[adjacentSideCCW];
289 const Color color(edge.color.red(), edge.color.green(), edge.color.blue(), a lpha);
290
291 bool usePath = m_outer.isRounded()
292 && (borderStyleHasInnerDetail(edge.borderStyle()) || m_hasRadii[side] || m_hasRadii[(side + 1) % 4]);
293
294 if (usePath) {
295 MitreType mitre1 = colorsMatchAtCorner(side, adjacentSideCCW, m_borderIn fo.edges)
296 ? NonAntiAliasedMitre : AntiAliasedMitre;
297 MitreType mitre2 = colorsMatchAtCorner(side, adjacentSideCW, m_borderInf o.edges)
298 ? NonAntiAliasedMitre : AntiAliasedMitre;
299
300 GraphicsContextStateSaver stateSaver(*context);
301 if (m_inner.isRenderable())
302 clipBorderSidePolygon(context, side, mitre1, mitre2);
303 else
304 BoxPainter::clipBorderSideForComplexInnerPath(context, m_outer, m_in ner, side, m_borderInfo.edges);
305 float thickness = std::max(std::max(edge.width, adjacentEdgeCCW.width), adjacentEdgeCW.width);
306 BoxPainter::drawBoxSideFromPath(context, LayoutRect(m_outer.rect()), m_r oundedPath, m_borderInfo.edges, edge.width, thickness, side, m_borderInfo.style,
307 color, edge.borderStyle(), m_borderInfo.bleedAvoidance, m_borderInfo .includeLogicalLeftEdge, m_borderInfo.includeLogicalRightEdge);
308 } else {
309 MitreType mitre1 = computeMitre(side, adjacentSideCW, paintedSides);
310 MitreType mitre2 = computeMitre(side, adjacentSideCCW, paintedSides);
311
312 bool clipForStyle = (mitre1 != NoMitre || mitre2 != NoMitre)
313 && styleRequiresClipPolygon(edge.borderStyle());
314 bool clipForMitre = mitre1 == NonAntiAliasedMitre || mitre2 == NonAntiAl iasedMitre;
315 bool shouldClip = clipForStyle || clipForMitre;
316
317 if (0) {
318 printf("*** side: %d, painted: %x, adjSide1: %d, mitre1: %d, adjSide 2: %d, mitre2: %d, clip: %d\n",
319 side, paintedSides, adjacentSideCW, mitre1, adjacentSideCCW, mit re2, shouldClip);
320 if (shouldClip) {
321 printf(" clipForStyle: %d, clipForMitre: %d\n", clipForStyle, c lipForMitre);
322 }
323 }
324
325 GraphicsContextStateSaver clipStateSaver(*context, shouldClip);
326 if (shouldClip) {
327 clipBorderSidePolygon(context, side, mitre2, mitre1);
328 // Since we clipped, no need to draw with a mitre.
329 mitre1 = mitre2 = NoMitre;
330 }
331
332 ASSERT(mitre1 == NoMitre || mitre1 == AntiAliasedMitre);
333 ASSERT(mitre2 == NoMitre || mitre2 == AntiAliasedMitre);
334
335 const FloatRect& borderRect = m_outer.rect();
336 const FloatSize edgeShift(borderRect.width() - edge.width, borderRect.he ight() - edge.width);
337 const FloatRect sideRect(
338 borderRect.x() + edgeShift.width() * kEdgeShiftMap[side][BSLeft],
339 borderRect.y() + edgeShift.height() * kEdgeShiftMap[side][BSTop],
340 borderRect.width() + edgeShift.width() * kEdgeShiftMap[side][BSRight ],
341 borderRect.height() + edgeShift.height() * kEdgeShiftMap[side][BSBot tom]);
342 ObjectPainter::drawLineForBoxSide(context, sideRect.x(), sideRect.y(), s ideRect.maxX(), sideRect.maxY(), side, color, edge.borderStyle(),
343 mitre2 != NoMitre ? adjacentEdgeCCW.width : 0, mitre1 != NoMitre ? a djacentEdgeCW.width : 0, true);
344 }
345 }
346
347 BoxBorderPainter::MitreType BoxBorderPainter::computeMitre(BoxSide side, BoxSide adjacentSide,
348 BorderEdgeFlags paintedSides) const
349 {
350 const BorderEdge& edge = m_borderInfo.edges[side];
351 const BorderEdge& adjacentEdge = m_borderInfo.edges[adjacentSide];
352
353 if (!adjacentEdge.usedWidth())
354 return NoMitre;
355
356 if (!includesEdge(paintedSides, adjacentSide) && borderStyleFillsBorderArea( adjacentEdge.borderStyle()))
357 return NoMitre;
358
359 if (!edge.sharesColorWith(adjacentEdge))
360 return AntiAliasedMitre;
361
362 if (borderStylesRequireMitre(side, adjacentSide, edge.borderStyle(), adjacen tEdge.borderStyle())) {
363 return borderStyleHasUnmatchedColorsAtCorner(edge.borderStyle(), side, a djacentSide)
364 ? AntiAliasedMitre : NonAntiAliasedMitre;
365 }
366
367 return NoMitre;
368 }
369
370 void BoxBorderPainter::clipBorderSidePolygon(GraphicsContext* context, BoxSide s ide,
371 MitreType mitre1, MitreType mitre2) const
372 {
373 ASSERT(mitre1 != NoMitre || mitre2 != NoMitre);
374
375 FloatPoint quad[4];
376
377 const LayoutRect outerRect(m_outer.rect());
378 const LayoutRect innerRect(m_inner.rect());
379
380 // For each side, create a quad that encompasses all parts of that side that may draw,
381 // including areas inside the innerBorder.
382 //
383 // 0----------------3
384 // 0 \ / 0
385 // |\ 1----------- 2 /|
386 // | 1 1 |
387 // | | | |
388 // | | | |
389 // | 2 2 |
390 // |/ 1------------2 \|
391 // 3 / \ 3
392 // 0----------------3
393 //
394 switch (side) {
395 case BSTop:
396 quad[0] = FloatPoint(outerRect.minXMinYCorner());
397 quad[1] = FloatPoint(innerRect.minXMinYCorner());
398 quad[2] = FloatPoint(innerRect.maxXMinYCorner());
399 quad[3] = FloatPoint(outerRect.maxXMinYCorner());
400
401 if (!m_inner.radii().topLeft().isZero()) {
402 findIntersection(quad[0], quad[1],
403 FloatPoint(
404 quad[1].x() + m_inner.radii().topLeft().width(),
405 quad[1].y()),
406 FloatPoint(
407 quad[1].x(),
408 quad[1].y() + m_inner.radii().topLeft().height()),
409 quad[1]);
410 }
411
412 if (!m_inner.radii().topRight().isZero()) {
413 findIntersection(quad[3], quad[2],
414 FloatPoint(
415 quad[2].x() - m_inner.radii().topRight().width(),
416 quad[2].y()),
417 FloatPoint(
418 quad[2].x(),
419 quad[2].y() + m_inner.radii().topRight().height()),
420 quad[2]);
421 }
422
423 if (mitre1 == NoMitre)
424 quad[1].setX(quad[0].x());
425 if (mitre2 == NoMitre)
426 quad[2].setX(quad[3].x());
427
428 break;
429
430 case BSLeft:
431 quad[0] = FloatPoint(outerRect.minXMinYCorner());
432 quad[1] = FloatPoint(innerRect.minXMinYCorner());
433 quad[2] = FloatPoint(innerRect.minXMaxYCorner());
434 quad[3] = FloatPoint(outerRect.minXMaxYCorner());
435
436 if (!m_inner.radii().topLeft().isZero()) {
437 findIntersection(quad[0], quad[1],
438 FloatPoint(
439 quad[1].x() + m_inner.radii().topLeft().width(),
440 quad[1].y()),
441 FloatPoint(
442 quad[1].x(),
443 quad[1].y() + m_inner.radii().topLeft().height()),
444 quad[1]);
445 }
446
447 if (!m_inner.radii().bottomLeft().isZero()) {
448 findIntersection(quad[3], quad[2],
449 FloatPoint(
450 quad[2].x() + m_inner.radii().bottomLeft().width(),
451 quad[2].y()),
452 FloatPoint(
453 quad[2].x(),
454 quad[2].y() - m_inner.radii().bottomLeft().height()),
455 quad[2]);
456 }
457
458 if (mitre1 == NoMitre)
459 quad[1].setY(quad[0].y());
460 if (mitre2 == NoMitre)
461 quad[2].setY(quad[3].y());
462
463 break;
464
465 case BSBottom:
466 quad[0] = FloatPoint(outerRect.minXMaxYCorner());
467 quad[1] = FloatPoint(innerRect.minXMaxYCorner());
468 quad[2] = FloatPoint(innerRect.maxXMaxYCorner());
469 quad[3] = FloatPoint(outerRect.maxXMaxYCorner());
470
471 if (!m_inner.radii().bottomLeft().isZero()) {
472 findIntersection(quad[0], quad[1],
473 FloatPoint(
474 quad[1].x() + m_inner.radii().bottomLeft().width(),
475 quad[1].y()),
476 FloatPoint(
477 quad[1].x(),
478 quad[1].y() - m_inner.radii().bottomLeft().height()),
479 quad[1]);
480 }
481
482 if (!m_inner.radii().bottomRight().isZero()) {
483 findIntersection(quad[3], quad[2],
484 FloatPoint(
485 quad[2].x() - m_inner.radii().bottomRight().width(),
486 quad[2].y()),
487 FloatPoint(
488 quad[2].x(),
489 quad[2].y() - m_inner.radii().bottomRight().height()),
490 quad[2]);
491 }
492
493 if (mitre1 == NoMitre)
494 quad[1].setX(quad[0].x());
495 if (mitre2 == NoMitre)
496 quad[2].setX(quad[3].x());
497
498 break;
499
500 case BSRight:
501 quad[0] = FloatPoint(outerRect.maxXMinYCorner());
502 quad[1] = FloatPoint(innerRect.maxXMinYCorner());
503 quad[2] = FloatPoint(innerRect.maxXMaxYCorner());
504 quad[3] = FloatPoint(outerRect.maxXMaxYCorner());
505
506 if (!m_inner.radii().topRight().isZero()) {
507 findIntersection(quad[0], quad[1],
508 FloatPoint(
509 quad[1].x() - m_inner.radii().topRight().width(),
510 quad[1].y()),
511 FloatPoint(
512 quad[1].x(),
513 quad[1].y() + m_inner.radii().topRight().height()),
514 quad[1]);
515 }
516
517 if (!m_inner.radii().bottomRight().isZero()) {
518 findIntersection(quad[3], quad[2],
519 FloatPoint(
520 quad[2].x() - m_inner.radii().bottomRight().width(),
521 quad[2].y()),
522 FloatPoint(
523 quad[2].x(),
524 quad[2].y() - m_inner.radii().bottomRight().height()),
525 quad[2]);
526 }
527
528 if (mitre1 == NoMitre)
529 quad[1].setY(quad[0].y());
530 if (mitre2 == NoMitre)
531 quad[2].setY(quad[3].y());
532
533 break;
534 }
535
536 bool antiAliasEdge1 = mitre1 != NonAntiAliasedMitre;
537 bool antiAliasEdge2 = mitre2 != NonAntiAliasedMitre;
538 if (antiAliasEdge1 == antiAliasEdge2) {
539 context->clipPolygon(4, quad, antiAliasEdge1);
540 return;
541 }
542
543 // If antialiasing settings for the first edge and second edge is different,
544 // they have to be addressed separately. We do this by breaking the quad int o
545 // two parallelograms, made by moving quad[1] and quad[2].
546 float ax = quad[1].x() - quad[0].x();
547 float ay = quad[1].y() - quad[0].y();
548 float bx = quad[2].x() - quad[1].x();
549 float by = quad[2].y() - quad[1].y();
550 float cx = quad[3].x() - quad[2].x();
551 float cy = quad[3].y() - quad[2].y();
552
553 const static float kEpsilon = 1e-2f;
554 float r1, r2;
555 if (fabsf(bx) < kEpsilon && fabsf(by) < kEpsilon) {
556 // The quad was actually a triangle.
557 r1 = r2 = 1.0f;
558 } else {
559 // Extend parallelogram a bit to hide calculation error
560 const static float kExtendFill = 1e-2f;
561
562 r1 = (-ax * by + ay * bx) / (cx * by - cy * bx) + kExtendFill;
563 r2 = (-cx * by + cy * bx) / (ax * by - ay * bx) + kExtendFill;
564 }
565
566 if (mitre1 != NoMitre) {
567 FloatPoint firstQuad[4];
568 firstQuad[0] = quad[0];
569 firstQuad[1] = quad[1];
570 firstQuad[2] = FloatPoint(quad[3].x() + r2 * ax, quad[3].y() + r2 * ay);
571 firstQuad[3] = quad[3];
572 context->clipPolygon(4, firstQuad, antiAliasEdge1);
573 }
574
575 if (mitre2 != NoMitre) {
576 FloatPoint secondQuad[4];
577 secondQuad[0] = quad[0];
578 secondQuad[1] = FloatPoint(quad[0].x() - r1 * cx, quad[0].y() - r1 * cy) ;
579 secondQuad[2] = quad[2];
580 secondQuad[3] = quad[3];
581 context->clipPolygon(4, secondQuad, antiAliasEdge2);
582 }
583 }
584
585 } // namespace blink
OLDNEW
« no previous file with comments | « Source/core/paint/BoxBorderPainter.h ('k') | Source/core/paint/BoxPainter.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698