/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/** * nsCSSRendering::PaintBorder * nsCSSRendering::PaintOutline * -> DrawBorders * * DrawBorders * -> Ability to use specialized approach? * |- Draw using specialized function * |- separate corners? * |- dashed side mask * | * -> can border be drawn in 1 pass? (e.g., solid border same color all * around) * |- DrawBorderSides with all 4 sides * -> more than 1 pass? * |- for each corner * |- clip to DoCornerClipSubPath * |- for each side adjacent to corner * |- clip to GetSideClipSubPath * |- DrawBorderSides with one side * |- for each side * |- GetSideClipWithoutCornersRect * |- DrawDashedOrDottedSide || DrawBorderSides with one side
*/
// given a side index, get the previous and next side index #define NEXT_SIDE(_s) mozilla::Side(((_s) + 1) & 3) #define PREV_SIDE(_s) mozilla::Side(((_s) + 3) & 3)
// given a corner index, get the previous and next corner index #define NEXT_CORNER(_s) Corner(((_s) + 1) & 3) #define PREV_CORNER(_s) Corner(((_s) + 3) & 3)
// from the given base color and the background color, turn // color into a color for the given border pattern style static sRGBColor MakeBorderColor(nscolor aColor,
BorderColorStyle aBorderColorStyle);
// Given a line index (an index starting from the outside of the // border going inwards) and an array of line styles, calculate the // color that that stripe of the border should be rendered in. static sRGBColor ComputeColorForLine(uint32_t aLineIndex, const BorderColorStyle* aBorderColorStyle,
uint32_t aBorderColorStyleCount,
nscolor aBorderColor);
// little helper function to check if the array of 4 floats given are // equal to the given value staticbool CheckFourFloatsEqual(constFloat* vals, Float k) { return (vals[0] == k && vals[1] == k && vals[2] == k && vals[3] == k);
}
typedefenum { // Normal solid square corner. Will be rectangular, the size of the // adjacent sides. If the corner has a border radius, the corner // will always be solid, since we don't do dotted/dashed etc.
CORNER_NORMAL,
// Paint the corner in whatever style is not dotted/dashed of the // adjacent corners.
CORNER_SOLID,
// Paint the corner as a dot, the size of the bigger of the adjacent // sides.
CORNER_DOT
} CornerStyle;
if (nsCSSBorderRenderer::AllCornersZeroSize(aRadii)) { // These will always be in pixel units from CSS
(*aDimsRet)[C_TL] = Size(leftWidth, topWidth);
(*aDimsRet)[C_TR] = Size(rightWidth, topWidth);
(*aDimsRet)[C_BR] = Size(rightWidth, bottomWidth);
(*aDimsRet)[C_BL] = Size(leftWidth, bottomWidth);
} else { // Always round up to whole pixels for the corners; it's safe to // make the corners bigger than necessary, and this way we ensure // that we avoid seams.
(*aDimsRet)[C_TL] = Size(ceil(std::max(leftWidth, aRadii[C_TL].width)),
ceil(std::max(topWidth, aRadii[C_TL].height)));
(*aDimsRet)[C_TR] = Size(ceil(std::max(rightWidth, aRadii[C_TR].width)),
ceil(std::max(topWidth, aRadii[C_TR].height)));
(*aDimsRet)[C_BR] = Size(ceil(std::max(rightWidth, aRadii[C_BR].width)),
ceil(std::max(bottomWidth, aRadii[C_BR].height)));
(*aDimsRet)[C_BL] = Size(ceil(std::max(leftWidth, aRadii[C_BL].width)),
ceil(std::max(bottomWidth, aRadii[C_BL].height)));
}
}
/* First check if the specified styles and colors are the same for all sides
*/ int firstStyle = 0; for (constauto i : mozilla::AllPhysicalSides()) { if (firstStyle == i) { if ((static_cast<mozilla::SideBits>(1 << i) & aSides) ==
SideBits::eNone) {
firstStyle++;
} continue;
}
/* Then if it's one of the two-tone styles and we're not
* just comparing the TL or BR sides */ switch (mBorderStyles[firstStyle]) { case StyleBorderStyle::Groove: case StyleBorderStyle::Ridge: case StyleBorderStyle::Inset: case StyleBorderStyle::Outset: return ((aSides & ~(SideBits::eTop | SideBits::eLeft)) ==
SideBits::eNone ||
(aSides & ~(SideBits::eBottom | SideBits::eRight)) ==
SideBits::eNone); default: returntrue;
}
}
BorderColorStyle nsCSSBorderRenderer::BorderColorStyleForSolidCorner(
StyleBorderStyle aStyle, Corner aCorner) { // note that this function assumes that the corner is already solid, // as per the earlier function switch (aStyle) { case StyleBorderStyle::Solid: case StyleBorderStyle::Double: return BorderColorStyleSolid;
case StyleBorderStyle::Inset: case StyleBorderStyle::Groove: if (aCorner == eCornerTopLeft) { return BorderColorStyleDark;
} elseif (aCorner == eCornerBottomRight) { return BorderColorStyleLight;
} break;
case StyleBorderStyle::Outset: case StyleBorderStyle::Ridge: if (aCorner == eCornerTopLeft) { return BorderColorStyleLight;
} elseif (aCorner == eCornerBottomRight) { return BorderColorStyleDark;
} break; default: return BorderColorStyleNone;
}
return BorderColorStyleNone;
}
Rect nsCSSBorderRenderer::GetCornerRect(Corner aCorner) {
Point offset(0.f, 0.f);
Rect nsCSSBorderRenderer::GetSideClipWithoutCornersRect(mozilla::Side aSide) {
Point offset(0.f, 0.f);
// The offset from the outside rect to the start of this side's // box. For the top and bottom sides, the height of the box // must be the border height; the x start must take into account // the corner size (which may be bigger than the right or left // side's width). The same applies to the right and left sides. if (aSide == eSideTop) {
offset.x = mBorderCornerDimensions[C_TL].width;
} elseif (aSide == eSideRight) {
offset.x = mOuterRect.Width() - mBorderWidths[eSideRight];
offset.y = mBorderCornerDimensions[C_TR].height;
} elseif (aSide == eSideBottom) {
offset.x = mBorderCornerDimensions[C_BL].width;
offset.y = mOuterRect.Height() - mBorderWidths[eSideBottom];
} elseif (aSide == eSideLeft) {
offset.y = mBorderCornerDimensions[C_TL].height;
}
// The sum of the width & height of the corners adjacent to the // side. This relies on the relationship between side indexing and // corner indexing; that is, 0 == SIDE_TOP and 0 == CORNER_TOP_LEFT, // with both proceeding clockwise.
Size sideCornerSum = mBorderCornerDimensions[GetCCWCorner(aSide)] +
mBorderCornerDimensions[GetCWCorner(aSide)];
Rect rect(mOuterRect.TopLeft() + offset, mOuterRect.Size() - sideCornerSum);
// The side border type and the adjacent border types are // examined and one of the different types of clipping (listed // below) is selected.
typedefenum { // clip to the trapezoid formed by the corners of the // inner and outer rectangles for the given side // // +--------------- // |\%%%%%%%%%%%%%% // | \%%%%%%%%%%%% // | \%%%%%%%%%%% // | \%%%%%%%%% // | +-------- // | | // | |
SIDE_CLIP_TRAPEZOID,
// clip to the trapezoid formed by the outer rectangle // corners and the center of the region, making sure // that diagonal lines all go directly from the outside // corner to the inside corner, but that they then continue on // to the middle. // // This is needed for correctly clipping rounded borders, // which might extend past the SIDE_CLIP_TRAPEZOID trap. // // +-------__--+--- // \%%%%_-%%%%%%%% // \+-%%%%%%%%%% // / \%%%%%%%%%% // / \%%%%%%%%% // | +%%_-+--- // | +%%%%%% // | / \%%%%% // + + \%%% // | | +-
SIDE_CLIP_TRAPEZOID_FULL,
// clip to the rectangle formed by the given side including corner. // This is used by the non-dotted side next to dotted side. // // +--------------- // |%%%%%%%%%%%%%%% // |%%%%%%%%%%%%%%% // |%%%%%%%%%%%%%%% // |%%%%%%%%%%%%%%% // +------+-------- // | | // | |
SIDE_CLIP_RECTANGLE_CORNER,
// clip to the rectangle formed by the given side excluding corner. // This is used by the dotted side next to non-dotted side. // // +------+-------- // | |%%%%%%%% // | |%%%%%%%% // | |%%%%%%%% // | |%%%%%%%% // | +-------- // | | // | |
SIDE_CLIP_RECTANGLE_NO_CORNER,
} SideClipType;
// Given three points, p0, p1, and midPoint, move p1 further in to the // rectangle (of which aMidPoint is the center) so that it reaches the // closer of the horizontal or vertical lines intersecting the midpoint, // while maintaing the slope of the line. If p0 and p1 are the same, // just move p1 to midPoint (since there's no slope to maintain). // FIXME: Extending only to the midpoint isn't actually sufficient for // boxes with asymmetric radii. staticvoid MaybeMoveToMidPoint(Point& aP0, Point& aP1, const Point& aMidPoint) {
Point ps = aP1 - aP0;
already_AddRefed<Path> nsCSSBorderRenderer::GetSideClipSubPath(
mozilla::Side aSide) { // the clip proceeds clockwise from the top left corner; // so "start" in each case is the start of the region from that side. // // the final path will be formed like: // s0 ------- e0 // | / // s1 ----- e1 // // that is, the second point will always be on the inside
Point nsCSSBorderRenderer::GetStraightBorderPoint(mozilla::Side aSide,
Corner aCorner, bool* aIsUnfilled, Float aDotOffset) { // Calculate the end point of the side for dashed/dotted border, that is also // the end point of the corner curve. The point is specified by aSide and // aCorner. (e.g. eSideTop and C_TL means the left end of border-top) // // // aCorner aSide // +-------------------- // | // | // | +---------- // | the end point // | // | +---------- // | | // | | // | | // // The position of the point depends on the border-style, border-width, and // border-radius of the side, corner, and the adjacent side beyond the corner, // to make those sides (and corner) interact well. // // If the style of aSide is dotted and the dot at the point should be // unfilled, true is stored to *aIsUnfilled, otherwise false is stored.
void nsCSSBorderRenderer::FillSolidBorder(const Rect& aOuterRect, const Rect& aInnerRect, const RectCornerRadii& aBorderRadii, constFloat* aBorderSizes,
SideBits aSides, const ColorPattern& aColor) { // Note that this function is allowed to draw more than just the // requested sides.
// If we have a border radius, do full rounded rectangles // and fill, regardless of what sides we're asked to draw. if (!AllCornersZeroSize(aBorderRadii)) {
RefPtr<PathBuilder> builder = mDrawTarget->CreatePathBuilder();
// do the outer border
AppendRoundedRectToPath(builder, aOuterRect, aBorderRadii, true);
// then do the inner border CCW
AppendRoundedRectToPath(builder, aInnerRect, innerRadii, false);
RefPtr<Path> path = builder->Finish();
mDrawTarget->Fill(path, aColor); return;
}
// If we're asked to draw all sides of an equal-sized border, // stroking is fastest. This is a fairly common path, but partial // sides is probably second in the list -- there are a bunch of // common border styles, such as inset and outset, that are // top-left/bottom-right split. if (aSides == SideBits::eAll &&
CheckFourFloatsEqual(aBorderSizes, aBorderSizes[0]) && !mAvoidStroke) { Float strokeWidth = aBorderSizes[0];
Rect r(aOuterRect);
r.Deflate(strokeWidth / 2.f);
mDrawTarget->StrokeRect(r, aColor, StrokeOptions(strokeWidth)); return;
}
// Otherwise, we have unequal sized borders or we're only // drawing some sides; create rectangles for each side // and fill them.
Rect r[4];
// compute base rects for each side if (aSides & SideBits::eTop) {
r[eSideTop] = Rect(aOuterRect.X(), aOuterRect.Y(), aOuterRect.Width(),
aBorderSizes[eSideTop]);
}
// If two sides meet at a corner that we're rendering, then // make sure that we adjust one of the sides to avoid overlap. // This is especially important in the case of colors with // an alpha channel.
if ((aSides & (SideBits::eTop | SideBits::eLeft)) ==
(SideBits::eTop | SideBits::eLeft)) { // adjust the left's top down a bit
r[eSideLeft].y += aBorderSizes[eSideTop];
r[eSideLeft].height -= aBorderSizes[eSideTop];
}
if ((aSides & (SideBits::eTop | SideBits::eRight)) ==
(SideBits::eTop | SideBits::eRight)) { // adjust the top's left a bit
r[eSideTop].width -= aBorderSizes[eSideRight];
}
if ((aSides & (SideBits::eBottom | SideBits::eRight)) ==
(SideBits::eBottom | SideBits::eRight)) { // adjust the right's bottom a bit
r[eSideRight].height -= aBorderSizes[eSideBottom];
}
if ((aSides & (SideBits::eBottom | SideBits::eLeft)) ==
(SideBits::eBottom | SideBits::eLeft)) { // adjust the bottom's left a bit
r[eSideBottom].x += aBorderSizes[eSideLeft];
r[eSideBottom].width -= aBorderSizes[eSideLeft];
}
// Filling these one by one is faster than filling them all at once. for (uint32_t i = 0; i < 4; i++) { if (aSides & static_cast<mozilla::SideBits>(1 << i)) {
MaybeSnapToDevicePixels(r[i], *mDrawTarget, true);
mDrawTarget->FillRect(r[i], aColor);
}
}
}
sRGBColor MakeBorderColor(nscolor aColor, BorderColorStyle aBorderColorStyle) {
nscolor colors[2]; int k = 0;
switch (aBorderColorStyle) { case BorderColorStyleNone: return sRGBColor(0.f, 0.f, 0.f, 0.f); // transparent black
case BorderColorStyleLight:
k = 1;
[[fallthrough]]; case BorderColorStyleDark:
NS_GetSpecial3DColors(colors, aColor); return sRGBColor::FromABGR(colors[k]);
case BorderColorStyleSolid: default: return sRGBColor::FromABGR(aColor);
}
}
// The borderColorStyle array goes from the outer to the inner style. // // If the border width is 1, we need to change the borderRenderStyle // a bit to make sure that we get the right colors -- e.g. 'ridge' // with a 1px border needs to look like solid, not like 'outset'. if (mOneUnitBorder && (borderRenderStyle == StyleBorderStyle::Ridge ||
borderRenderStyle == StyleBorderStyle::Groove ||
borderRenderStyle == StyleBorderStyle::Double)) {
borderRenderStyle = StyleBorderStyle::Solid;
}
switch (borderRenderStyle) { case StyleBorderStyle::Solid:
borderColorStyleTopLeft[0] = BorderColorStyleSolid;
// The only way to get to here is by having a // borderColorStyleCount < 1 or > 3; this should never happen, // since -moz-border-colors doesn't get handled here.
NS_ASSERTION(borderColorStyleCount > 0 && borderColorStyleCount < 4, "Non-border-colors case with borderColorStyleCount < 1 or > 3; " "what happened?");
// The caller should never give us anything with a mix // of TL/BR if the border style would require a // TL/BR split. if (aSides & (SideBits::eBottom | SideBits::eRight)) {
borderColorStyle = borderColorStyleBottomRight;
} else {
borderColorStyle = borderColorStyleTopLeft;
}
// Distribute the border across the available space. Float borderWidths[3][4];
if (borderColorStyleCount == 1) { for (constauto i : mozilla::AllPhysicalSides()) {
borderWidths[0][i] = mBorderWidths[i];
}
} elseif (borderColorStyleCount == 2) { // with 2 color styles, any extra pixel goes to the outside for (constauto i : mozilla::AllPhysicalSides()) {
borderWidths[0][i] =
int32_t(mBorderWidths[i]) / 2 + int32_t(mBorderWidths[i]) % 2;
borderWidths[1][i] = int32_t(mBorderWidths[i]) / 2;
}
} elseif (borderColorStyleCount == 3) { // with 3 color styles, any extra pixel (or lack of extra pixel) // goes to the middle for (constauto i : mozilla::AllPhysicalSides()) { if (mBorderWidths[i] == 1.0) {
borderWidths[0][i] = 1.f;
borderWidths[1][i] = borderWidths[2][i] = 0.f;
} else {
int32_t rest = int32_t(mBorderWidths[i]) % 3;
borderWidths[0][i] = borderWidths[2][i] = borderWidths[1][i] =
(int32_t(mBorderWidths[i]) - rest) / 3;
// make a copy that we can modify
RectCornerRadii radii = mBorderRadii;
Rect soRect(mOuterRect);
Rect siRect(mOuterRect);
// If adjacent side is dotted and radius=0, draw side to the end of corner. // // +-------------------------------- // |################################ // | // |################################ // +-----+-------------------------- // | | // | | // | | // | | // | | // | ### | // |#####| // |#####| // |#####| // | ### | // | | bool noMarginTop = false; bool noMarginRight = false; bool noMarginBottom = false; bool noMarginLeft = false;
// If there is at least one dotted side, every side is rendered separately. if (IsSingleSide(aSides)) { if (aSides == SideBits::eTop) { if (mBorderStyles[eSideRight] == StyleBorderStyle::Dotted &&
IsZeroSize(mBorderRadii[C_TR])) {
noMarginRight = true;
} if (mBorderStyles[eSideLeft] == StyleBorderStyle::Dotted &&
IsZeroSize(mBorderRadii[C_TL])) {
noMarginLeft = true;
}
} elseif (aSides == SideBits::eRight) { if (mBorderStyles[eSideTop] == StyleBorderStyle::Dotted &&
IsZeroSize(mBorderRadii[C_TR])) {
noMarginTop = true;
} if (mBorderStyles[eSideBottom] == StyleBorderStyle::Dotted &&
IsZeroSize(mBorderRadii[C_BR])) {
noMarginBottom = true;
}
} elseif (aSides == SideBits::eBottom) { if (mBorderStyles[eSideRight] == StyleBorderStyle::Dotted &&
IsZeroSize(mBorderRadii[C_BR])) {
noMarginRight = true;
} if (mBorderStyles[eSideLeft] == StyleBorderStyle::Dotted &&
IsZeroSize(mBorderRadii[C_BL])) {
noMarginLeft = true;
}
} else { if (mBorderStyles[eSideTop] == StyleBorderStyle::Dotted &&
IsZeroSize(mBorderRadii[C_TL])) {
noMarginTop = true;
} if (mBorderStyles[eSideBottom] == StyleBorderStyle::Dotted &&
IsZeroSize(mBorderRadii[C_BL])) {
noMarginBottom = true;
}
}
}
for (unsignedint i = 0; i < borderColorStyleCount; i++) { // walk siRect inwards at the start of the loop to get the // correct inner rect. // // If noMarginTop is false: // --------------------+ // /| // / | // L | // ----------------+ | // | | // | | // // If noMarginTop is true: // ----------------+<--+ // | | // | | // | | // | | // | | // | |
siRect.Deflate(Margin(noMarginTop ? 0 : borderWidths[i][0],
noMarginRight ? 0 : borderWidths[i][1],
noMarginBottom ? 0 : borderWidths[i][2],
noMarginLeft ? 0 : borderWidths[i][3]));
if (borderColorStyle[i] != BorderColorStyleNone) {
sRGBColor c = ComputeColorForLine(
i, borderColorStyle, borderColorStyleCount, borderRenderColor);
ColorPattern color(ToDeviceColor(c));
// Dashed line starts and ends with half segment in most case. // // __--+---+---+---+---+---+---+---+---+--__ // |###| | |###|###| | |###| // |###| | |###|###| | |###| // |###| | |###|###| | |###| // __--+---+---+---+---+---+---+---+---+--__ // // If radius=0 and other side is either dotted or 0-width, it starts or ends // with full segment. // // +---+---+---+---+---+---+---+---+---+---+ // |###|###| | |###|###| | |###|###| // |###|###| | |###|###| | |###|###| // |###|###| | |###|###| | |###|###| // +---++--+---+---+---+---+---+---+--++---+ // | | | | // | | | | // | | | | // | | | | // | ## | | ## | // |####| |####| // |####| |####| // | ## | | ## | // | | | | bool fullStart = false, fullEnd = false; Float halfDash; if (style == StyleBorderStyle::Dashed) { // If either end of the side is not connecting onto a corner then we want a // full dash at that end. // // Note that in the case that a corner is empty, either the adjacent side // has zero width, or else DrawBorders() set the corner to be empty // (it does that if the adjacent side has zero length and the border widths // of this and the adjacent sides are thin enough that the corner will be // insignificantly small).
void nsCSSBorderRenderer::DrawDashedOrDottedSide(mozilla::Side aSide) { // Draw dashed/dotted side with following approach. // // dashed side // Draw dashed line along the side, with appropriate dash length and gap // to make the side symmetric as far as possible. Dash length equals to // the gap, and the ratio of the dash length to border-width is the maximum // value in in [1, 3] range. // In most case, line ends with half segment, to joint with corner easily. // If adjacent side is dotted or 0px and border-radius for the corner // between them is 0, the line ends with full segment. // (see comment for GetStraightBorderPoint for more detail) // // dotted side // If border-width <= 2.0, draw 1:1 dashed line. // Otherwise, draw circles along the side, with appropriate gap that makes // the side symmetric as far as possible. The ratio of the gap to // border-width is the maximum value in [0.5, 1] range in most case. // if the side is too short and there's only 2 dots, it can be more smaller. // If there's no space to place 2 dots at the side, draw single dot at the // middle of the side. // In most case, line ends with filled dot, to joint with corner easily, // If adjacent side is dotted with larger border-width, or other style, // the line ends with unfilled dot. // (see comment for GetStraightBorderPoint for more detail)
NS_ASSERTION(mBorderStyles[aSide] == StyleBorderStyle::Dashed ||
mBorderStyles[aSide] == StyleBorderStyle::Dotted, "Style should be dashed or dotted.");
nscolor borderColor = mBorderColors[aSide]; bool ignored; // Get the start and end points of the side, ensuring that any dot origins get // pushed outward to account for stroking.
Point start =
GetStraightBorderPoint(aSide, GetCCWCorner(aSide), &ignored, 0.5f);
Point end = GetStraightBorderPoint(aSide, GetCWCorner(aSide), &ignored, 0.5f); if (borderWidth < 2.0f) { // Round start to draw dot on each pixel. if (IsHorizontalSide(aSide)) {
start.x = round(start.x);
} else {
start.y = round(start.y);
}
}
// For dotted sides that can merge with their prior dotted sides, advance the // dash offset to measure the distance around the combined path. This prevents // two dots from bunching together at a corner.
mozilla::Side mergeSide = aSide; while (IsCornerMergeable(GetCCWCorner(mergeSide))) {
mergeSide = PREV_SIDE(mergeSide); // If we looped all the way around, measure starting at the top side, since // we need to pick a fixed location to start measuring distance from still. if (mergeSide == aSide) {
mergeSide = eSideTop; break;
}
} while (mergeSide != aSide) { // Measure the length of the merged side starting from a possibly // unmergeable corner up to the merged corner. A merged corner effectively // has no border radius, so we can just use the cheaper AtCorner to find the // end point. Float mergeLength =
GetBorderLength(mergeSide,
GetStraightBorderPoint(
mergeSide, GetCCWCorner(mergeSide), &ignored, 0.5f),
mOuterRect.AtCorner(GetCWCorner(mergeSide))); // Add in the merged side length. Also offset the dash progress by an extra // dot's width to avoid drawing a dot that would overdraw where the merged // side would have ended in a gap, i.e. O_O_ // O
strokeOptions.mDashOffset += mergeLength + borderWidth;
mergeSide = NEXT_SIDE(mergeSide);
}
void nsCSSBorderRenderer::DrawDottedSideSlow(mozilla::Side aSide) { // Draw each circles separately for dotted with borderWidth > 2.0. // Dashed line with CapStyle::ROUND doesn't render perfect circles.
NS_ASSERTION(mBorderStyles[aSide] == StyleBorderStyle::Dotted, "Style should be dotted.");
nscolor borderColor = mBorderColors[aSide]; bool isStartUnfilled, isEndUnfilled;
Point start =
GetStraightBorderPoint(aSide, GetCCWCorner(aSide), &isStartUnfilled);
Point end = GetStraightBorderPoint(aSide, GetCWCorner(aSide), &isEndUnfilled); enum { // Corner is not mergeable.
NO_MERGE,
// Corner between different colors. // Two dots are merged into one, and both side draw half dot.
MERGE_HALF,
// Corner between same colors, CCW corner of the side. // Two dots are merged into one, and this side draw entire dot. // // MERGE_ALL MERGE_NONE // | | // v v // +-----------------------+----+ // | ## ## ## | ## | // |#### #### #### |####| // |#### #### #### |####| // | ## ## ## | ## | // +----+------------------+ | // | | | | // | | | | // | | | | // | ## | | ## | // |####| |####|
MERGE_ALL,
// Corner between same colors, CW corner of the side. // Two dots are merged into one, and this side doesn't draw dot.
MERGE_NONE
} mergeStart = NO_MERGE,
mergeEnd = NO_MERGE;
if (IsCornerMergeable(GetCCWCorner(aSide))) { if (borderColor == mBorderColors[PREV_SIDE(aSide)]) {
mergeStart = MERGE_ALL;
} else {
mergeStart = MERGE_HALF;
}
}
if (IsCornerMergeable(GetCWCorner(aSide))) { if (borderColor == mBorderColors[NEXT_SIDE(aSide)]) {
mergeEnd = MERGE_NONE;
} else {
mergeEnd = MERGE_HALF;
}
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.