bool SkBlurImageFilter::filterImageGPU(Proxy* proxy, const SkBitmap& src, const Context& ctx, SkBitmap* result, SkIPoint* offset) const { #if SK_SUPPORT_GPU SkBitmap input = src; SkIPoint srcOffset = SkIPoint::Make(0, 0); if (getInput(0) && !getInput(0)->getInputResultGPU(proxy, src, ctx, &input, &srcOffset)) { return false; } SkIRect rect; if (!this->applyCropRect(ctx, proxy, input, &srcOffset, &rect, &input)) { return false; } GrTexture* source = input.getTexture(); SkVector sigma = SkVector::Make(fSigma.width(), fSigma.height()); ctx.ctm().mapVectors(&sigma, 1); sigma.fX = SkMinScalar(sigma.fX, MAX_SIGMA); sigma.fY = SkMinScalar(sigma.fY, MAX_SIGMA); offset->fX = rect.fLeft; offset->fY = rect.fTop; rect.offset(-srcOffset); SkAutoTUnref<GrTexture> tex(SkGpuBlurUtils::GaussianBlur(source->getContext(), source, false, SkRect::Make(rect), true, sigma.x(), sigma.y())); WrapTexture(tex, rect.width(), rect.height(), result); return true; #else SkDEBUGFAIL("Should not call in GPU-less build"); return false; #endif }
static SkScalar RGB_to_HSV(SkColor color, HSV_Choice choice) { SkScalar red = SkIntToScalar(SkColorGetR(color)); SkScalar green = SkIntToScalar(SkColorGetG(color)); SkScalar blue = SkIntToScalar(SkColorGetB(color)); SkScalar min = SkMinScalar(SkMinScalar(red, green), blue); SkScalar value = SkMaxScalar(SkMaxScalar(red, green), blue); if (choice == kGetValue) return value/255; SkScalar delta = value - min; SkScalar saturation = value == 0 ? 0 : SkScalarDiv(delta, value); if (choice == kGetSaturation) return saturation; SkScalar hue; if (saturation == 0) hue = 0; else { SkScalar part60 = SkScalarDiv(60 * SK_Scalar1, delta); if (red == value) { hue = SkScalarMul(green - blue, part60); if (hue < 0) hue += 360 * SK_Scalar1; } else if (green == value) hue = 120 * SK_Scalar1 + SkScalarMul(blue - red, part60); else // blue == value hue = 240 * SK_Scalar1 + SkScalarMul(red - green, part60); } SkASSERT(choice == kGetHue); return hue; }
static SkVector map_sigma(const SkSize& localSigma, const SkMatrix& ctm) { SkVector sigma = SkVector::Make(localSigma.width(), localSigma.height()); ctm.mapVectors(&sigma, 1); sigma.fX = SkMinScalar(SkScalarAbs(sigma.fX), MAX_SIGMA); sigma.fY = SkMinScalar(SkScalarAbs(sigma.fY), MAX_SIGMA); return sigma; }
bool SkRect::intersect(const SkRect& a, const SkRect& b) { SkASSERT(&a && &b); if (!a.isEmpty() && !b.isEmpty() && a.fLeft < b.fRight && b.fLeft < a.fRight && a.fTop < b.fBottom && b.fTop < a.fBottom) { fLeft = SkMaxScalar(a.fLeft, b.fLeft); fTop = SkMaxScalar(a.fTop, b.fTop); fRight = SkMinScalar(a.fRight, b.fRight); fBottom = SkMinScalar(a.fBottom, b.fBottom); return true; } return false; }
bool SkRect::intersect2(const SkRect& r) { SkASSERT(&r); SkScalar L = SkMaxScalar(fLeft, r.fLeft); SkScalar R = SkMinScalar(fRight, r.fRight); if (L >= R) { return false; } SkScalar T = SkMaxScalar(fTop, r.fTop); SkScalar B = SkMinScalar(fBottom, r.fBottom); if (T >= B) { return false; } this->set(L, T, R, B); return true; }
void SkRect::join(SkScalar left, SkScalar top, SkScalar right, SkScalar bottom) { // do nothing if the params are empty if (left >= right || top >= bottom) { return; } // if we are empty, just assign if (fLeft >= fRight || fTop >= fBottom) { this->set(left, top, right, bottom); } else { fLeft = SkMinScalar(fLeft, left); fTop = SkMinScalar(fTop, top); fRight = SkMaxScalar(fRight, right); fBottom = SkMaxScalar(fBottom, bottom); } }
bool SkBlurMaskFilterImpl::filterMask(SkMask* dst, const SkMask& src, const SkMatrix& matrix, SkIPoint* margin) { SkScalar radius; if (fBlurFlags & SkBlurMaskFilter::kIgnoreTransform_BlurFlag) radius = fRadius; else radius = matrix.mapRadius(fRadius); // To avoid unseemly allocation requests (esp. for finite platforms like // handset) we limit the radius so something manageable. (as opposed to // a request like 10,000) static const SkScalar MAX_RADIUS = SkIntToScalar(128); radius = SkMinScalar(radius, MAX_RADIUS); SkBlurMask::Quality blurQuality = (fBlurFlags & SkBlurMaskFilter::kHighQuality_BlurFlag) ? SkBlurMask::kHigh_Quality : SkBlurMask::kLow_Quality; if (SkBlurMask::Blur(dst, src, radius, (SkBlurMask::Style)fBlurStyle, blurQuality)) { if (margin) { // we need to integralize radius for our margin, so take the ceil // just to be safe. margin->set(SkScalarCeil(radius), SkScalarCeil(radius)); } return true; } return false; }
void SkRRect::setRectXY(const SkRect& rect, SkScalar xRad, SkScalar yRad) { if (rect.isEmpty() || !rect.isFinite()) { this->setEmpty(); return; } if (!SkScalarsAreFinite(xRad, yRad)) { xRad = yRad = 0; // devolve into a simple rect } if (xRad <= 0 || yRad <= 0) { // all corners are square in this case this->setRect(rect); return; } if (rect.width() < xRad+xRad || rect.height() < yRad+yRad) { SkScalar scale = SkMinScalar(rect.width() / (xRad + xRad), rect.height() / (yRad + yRad)); SkASSERT(scale < SK_Scalar1); xRad = SkScalarMul(xRad, scale); yRad = SkScalarMul(yRad, scale); } fRect = rect; for (int i = 0; i < 4; ++i) { fRadii[i].set(xRad, yRad); } fType = kSimple_Type; if (xRad >= SkScalarHalf(fRect.width()) && yRad >= SkScalarHalf(fRect.height())) { fType = kOval_Type; // TODO: assert that all the x&y radii are already W/2 & H/2 } SkDEBUGCODE(this->validate();) }
void onDrawContent(SkCanvas* canvas) override { SkScalar angle = fAngle*SK_ScalarPI + SkScalarHalf(SK_ScalarPI); SkPoint center = SkPoint::Make(SkScalarHalf(this->width()), SkScalarHalf(this->height())); SkScalar length = 5; SkScalar step = angle; SkPath path; path.moveTo(center); while (length < (SkScalarHalf(SkMinScalar(this->width(), this->height())) - 10.f)) { SkPoint rp = SkPoint::Make(length*SkScalarCos(step) + center.fX, length*SkScalarSin(step) + center.fY); path.lineTo(rp); length += SkScalarDiv(angle, SkScalarHalf(SK_ScalarPI)); step += angle; } path.close(); SkPaint paint; paint.setAntiAlias(true); paint.setStyle(SkPaint::kStroke_Style); paint.setColor(0xFF007700); canvas->drawPath(path, paint); }
void SkRRect::setNinePatch(const SkRect& rect, SkScalar leftRad, SkScalar topRad, SkScalar rightRad, SkScalar bottomRad) { if (rect.isEmpty() || !rect.isFinite()) { this->setEmpty(); return; } const SkScalar array[4] = { leftRad, topRad, rightRad, bottomRad }; if (!SkScalarsAreFinite(array, 4)) { this->setRect(rect); // devolve into a simple rect return; } leftRad = SkMaxScalar(leftRad, 0); topRad = SkMaxScalar(topRad, 0); rightRad = SkMaxScalar(rightRad, 0); bottomRad = SkMaxScalar(bottomRad, 0); SkScalar scale = SK_Scalar1; if (leftRad + rightRad > rect.width()) { scale = rect.width() / (leftRad + rightRad); } if (topRad + bottomRad > rect.height()) { scale = SkMinScalar(scale, rect.height() / (topRad + bottomRad)); } if (scale < SK_Scalar1) { leftRad = SkScalarMul(leftRad, scale); topRad = SkScalarMul(topRad, scale); rightRad = SkScalarMul(rightRad, scale); bottomRad = SkScalarMul(bottomRad, scale); } if (leftRad == rightRad && topRad == bottomRad) { if (leftRad >= SkScalarHalf(rect.width()) && topRad >= SkScalarHalf(rect.height())) { fType = kOval_Type; } else if (0 == leftRad || 0 == topRad) { // If the left and (by equality check above) right radii are zero then it is a rect. // Same goes for top/bottom. fType = kRect_Type; leftRad = 0; topRad = 0; rightRad = 0; bottomRad = 0; } else { fType = kSimple_Type; } } else { fType = kNinePatch_Type; } fRect = rect; fRadii[kUpperLeft_Corner].set(leftRad, topRad); fRadii[kUpperRight_Corner].set(rightRad, topRad); fRadii[kLowerRight_Corner].set(rightRad, bottomRad); fRadii[kLowerLeft_Corner].set(leftRad, bottomRad); SkDEBUGCODE(this->validate();) }
bool SkSliderView::onClick(Click* click) { if (fMax) { SkScalar percent = SkScalarDiv(click->fCurr.fX + SK_Scalar1, this->width() - SK_Scalar1*2); percent = SkMaxScalar(0, SkMinScalar(percent, SK_Scalar1)); this->setValue(SkScalarRound(percent * fMax)); return true; } return false; }
// static void SkPDFUtils::AppendRectangle(const SkRect& rect, SkWStream* content) { // Skia has 0,0 at top left, pdf at bottom left. Do the right thing. SkScalar bottom = SkMinScalar(rect.fBottom, rect.fTop); SkPDFScalar::Append(rect.fLeft, content); content->writeText(" "); SkPDFScalar::Append(bottom, content); content->writeText(" "); SkPDFScalar::Append(rect.width(), content); content->writeText(" "); SkPDFScalar::Append(rect.height(), content); content->writeText(" re\n"); }
GrTextureDomain::GrTextureDomain(const SkRect& domain, Mode mode, int index) : fIndex(index) { static const SkRect kFullRect = {0, 0, SK_Scalar1, SK_Scalar1}; if (domain.contains(kFullRect) && kClamp_Mode == mode) { fMode = kIgnore_Mode; } else { fMode = mode; } if (fMode != kIgnore_Mode) { // We don't currently handle domains that are empty or don't intersect the texture. // It is OK if the domain rect is a line or point, but it should not be inverted. We do not // handle rects that do not intersect the [0..1]x[0..1] rect. SkASSERT(domain.fLeft <= domain.fRight); SkASSERT(domain.fTop <= domain.fBottom); fDomain.fLeft = SkMaxScalar(domain.fLeft, kFullRect.fLeft); fDomain.fRight = SkMinScalar(domain.fRight, kFullRect.fRight); fDomain.fTop = SkMaxScalar(domain.fTop, kFullRect.fTop); fDomain.fBottom = SkMinScalar(domain.fBottom, kFullRect.fBottom); SkASSERT(fDomain.fLeft <= fDomain.fRight); SkASSERT(fDomain.fTop <= fDomain.fBottom); } }
Bounds bounds(const DrawPosTextH& op) const { const int N = op.paint.countText(op.text, op.byteLength); if (N == 0) { return Bounds::MakeEmpty(); } SkScalar left = op.xpos[0], right = op.xpos[0]; for (int i = 1; i < N; i++) { left = SkMinScalar(left, op.xpos[i]); right = SkMaxScalar(right, op.xpos[i]); } SkRect dst = { left, op.y, right, op.y }; AdjustTextForFontMetrics(&dst, op.paint); return this->adjustAndMap(dst, &op.paint); }
static sk_sp<SkImage> make_gradient_circle(int width, int height) { SkScalar x = SkIntToScalar(width / 2); SkScalar y = SkIntToScalar(height / 2); SkScalar radius = SkMinScalar(x, y) * 0.8f; auto surface(SkSurface::MakeRasterN32Premul(width, height)); SkCanvas* canvas = surface->getCanvas(); canvas->clear(0x00000000); SkColor colors[2]; colors[0] = SK_ColorWHITE; colors[1] = SK_ColorBLACK; SkPaint paint; paint.setShader(SkGradientShader::MakeRadial(SkPoint::Make(x, y), radius, colors, nullptr, 2, SkTileMode::kClamp)); canvas->drawCircle(x, y, radius, paint); return surface->makeImageSnapshot(); }
void SkPaint_Inflate(SkPaint* paint, const SkDOM& dom, const SkDOM::Node* node) { SkASSERT(paint); SkASSERT(&dom); SkASSERT(node); SkScalar x; if (dom.findScalar(node, "stroke-width", &x)) paint->setStrokeWidth(x); if (dom.findScalar(node, "text-size", &x)) paint->setTextSize(x); bool b; SkASSERT("legacy: use is-stroke" && !dom.findBool(node, "is-frame", &b)); if (dom.findBool(node, "is-stroke", &b)) paint->setStyle(b ? SkPaint::kStroke_Style : SkPaint::kFill_Style); if (dom.findBool(node, "is-antialias", &b)) paint->setAntiAlias(b); if (dom.findBool(node, "is-lineartext", &b)) paint->setLinearText(b); const char* str = dom.findAttr(node, "color"); if (str) { SkColor c = paint->getColor(); if (SkParse::FindColor(str, &c)) paint->setColor(c); } // do this AFTER parsing for the color if (dom.findScalar(node, "opacity", &x)) { x = SkMaxScalar(0, SkMinScalar(x, SK_Scalar1)); paint->setAlpha(SkScalarRound(x * 255)); } int index = dom.findList(node, "text-anchor", "left,center,right"); if (index >= 0) paint->setTextAlign((SkPaint::Align)index); SkShader* shader = inflate_shader(dom, node); if (shader) paint->setShader(shader)->unref(); }
void make_gradient_circle(int width, int height) { SkScalar x = SkIntToScalar(width / 2); SkScalar y = SkIntToScalar(height / 2); SkScalar radius = SkMinScalar(x, y) * 0.8f; fGradientCircle.allocN32Pixels(width, height); SkCanvas canvas(fGradientCircle); canvas.clear(0x00000000); SkColor colors[2]; colors[0] = SK_ColorWHITE; colors[1] = SK_ColorBLACK; SkAutoTUnref<SkShader> shader( SkGradientShader::CreateRadial(SkPoint::Make(x, y), radius, colors, NULL, 2, SkShader::kClamp_TileMode) ); SkPaint paint; paint.setShader(shader); canvas.drawCircle(x, y, radius, paint); }
static void generate_aa_fill_rect_geometry(intptr_t verts, size_t vertexStride, GrColor color, const SkMatrix& viewMatrix, const SkRect& rect, const SkRect& devRect, bool tweakAlphaForCoverage, const SkMatrix* localMatrix) { SkPoint* fan0Pos = reinterpret_cast<SkPoint*>(verts); SkPoint* fan1Pos = reinterpret_cast<SkPoint*>(verts + 4 * vertexStride); SkScalar inset; if (viewMatrix.rectStaysRect()) { inset = SkMinScalar(devRect.width(), SK_Scalar1); inset = SK_ScalarHalf * SkMinScalar(inset, devRect.height()); set_inset_fan(fan0Pos, vertexStride, devRect, -SK_ScalarHalf, -SK_ScalarHalf); set_inset_fan(fan1Pos, vertexStride, devRect, inset, inset); } else { // compute transformed (1, 0) and (0, 1) vectors SkVector vec[2] = {{viewMatrix[SkMatrix::kMScaleX], viewMatrix[SkMatrix::kMSkewY]}, {viewMatrix[SkMatrix::kMSkewX], viewMatrix[SkMatrix::kMScaleY]}}; SkScalar len1 = SkPoint::Normalize(&vec[0]); vec[0].scale(SK_ScalarHalf); SkScalar len2 = SkPoint::Normalize(&vec[1]); vec[1].scale(SK_ScalarHalf); inset = SkMinScalar(len1 * rect.width(), SK_Scalar1); inset = SK_ScalarHalf * SkMinScalar(inset, len2 * rect.height()); // create the rotated rect SkPointPriv::SetRectFan(fan0Pos, rect.fLeft, rect.fTop, rect.fRight, rect.fBottom, vertexStride); SkMatrixPriv::MapPointsWithStride(viewMatrix, fan0Pos, vertexStride, 4); // Now create the inset points and then outset the original // rotated points // TL *((SkPoint*)((intptr_t)fan1Pos + 0 * vertexStride)) = *((SkPoint*)((intptr_t)fan0Pos + 0 * vertexStride)) + vec[0] + vec[1]; *((SkPoint*)((intptr_t)fan0Pos + 0 * vertexStride)) -= vec[0] + vec[1]; // BL *((SkPoint*)((intptr_t)fan1Pos + 1 * vertexStride)) = *((SkPoint*)((intptr_t)fan0Pos + 1 * vertexStride)) + vec[0] - vec[1]; *((SkPoint*)((intptr_t)fan0Pos + 1 * vertexStride)) -= vec[0] - vec[1]; // BR *((SkPoint*)((intptr_t)fan1Pos + 2 * vertexStride)) = *((SkPoint*)((intptr_t)fan0Pos + 2 * vertexStride)) - vec[0] - vec[1]; *((SkPoint*)((intptr_t)fan0Pos + 2 * vertexStride)) += vec[0] + vec[1]; // TR *((SkPoint*)((intptr_t)fan1Pos + 3 * vertexStride)) = *((SkPoint*)((intptr_t)fan0Pos + 3 * vertexStride)) - vec[0] + vec[1]; *((SkPoint*)((intptr_t)fan0Pos + 3 * vertexStride)) += vec[0] - vec[1]; } if (localMatrix) { SkMatrix invViewMatrix; if (!viewMatrix.invert(&invViewMatrix)) { SkDebugf("View matrix is non-invertible, local coords will be wrong."); invViewMatrix = SkMatrix::I(); } SkMatrix localCoordMatrix; localCoordMatrix.setConcat(*localMatrix, invViewMatrix); SkPoint* fan0Loc = reinterpret_cast<SkPoint*>(verts + sizeof(SkPoint) + sizeof(GrColor)); SkMatrixPriv::MapPointsWithStride(localCoordMatrix, fan0Loc, vertexStride, fan0Pos, vertexStride, 8); } // Make verts point to vertex color and then set all the color and coverage vertex attrs // values. verts += sizeof(SkPoint); // The coverage offset is always the last vertex attribute intptr_t coverageOffset = vertexStride - sizeof(GrColor) - sizeof(SkPoint); for (int i = 0; i < 4; ++i) { if (tweakAlphaForCoverage) { *reinterpret_cast<GrColor*>(verts + i * vertexStride) = 0; } else { *reinterpret_cast<GrColor*>(verts + i * vertexStride) = color; *reinterpret_cast<float*>(verts + i * vertexStride + coverageOffset) = 0; } } int scale; if (inset < SK_ScalarHalf) { scale = SkScalarFloorToInt(512.0f * inset / (inset + SK_ScalarHalf)); SkASSERT(scale >= 0 && scale <= 255); } else { scale = 0xff; } verts += 4 * vertexStride; float innerCoverage = GrNormalizeByteToFloat(scale); GrColor scaledColor = (0xff == scale) ? color : SkAlphaMulQ(color, scale); for (int i = 0; i < 4; ++i) { if (tweakAlphaForCoverage) { *reinterpret_cast<GrColor*>(verts + i * vertexStride) = scaledColor; } else { *reinterpret_cast<GrColor*>(verts + i * vertexStride) = color; *reinterpret_cast<float*>(verts + i * vertexStride + coverageOffset) = innerCoverage; } } }
void SkRRect::setRectRadii(const SkRect& rect, const SkVector radii[4]) { if (rect.isEmpty()) { this->setEmpty(); return; } fRect = rect; memcpy(fRadii, radii, sizeof(fRadii)); bool allCornersSquare = true; // Clamp negative radii to zero for (int i = 0; i < 4; ++i) { if (fRadii[i].fX <= 0 || fRadii[i].fY <= 0) { // In this case we are being a little fast & loose. Since one of // the radii is 0 the corner is square. However, the other radii // could still be non-zero and play in the global scale factor // computation. fRadii[i].fX = 0; fRadii[i].fY = 0; } else { allCornersSquare = false; } } if (allCornersSquare) { this->setRect(rect); return; } // Proportionally scale down all radii to fit. Find the minimum ratio // of a side and the radii on that side (for all four sides) and use // that to scale down _all_ the radii. This algorithm is from the // W3 spec (http://www.w3.org/TR/css3-background/) section 5.5 - Overlapping // Curves: // "Let f = min(Li/Si), where i is one of { top, right, bottom, left }, // Si is the sum of the two corresponding radii of the corners on side i, // and Ltop = Lbottom = the width of the box, // and Lleft = Lright = the height of the box. // If f < 1, then all corner radii are reduced by multiplying them by f." SkScalar scale = SK_Scalar1; if (fRadii[0].fX + fRadii[1].fX > rect.width()) { scale = SkMinScalar(scale, SkScalarDiv(rect.width(), fRadii[0].fX + fRadii[1].fX)); } if (fRadii[1].fY + fRadii[2].fY > rect.height()) { scale = SkMinScalar(scale, SkScalarDiv(rect.height(), fRadii[1].fY + fRadii[2].fY)); } if (fRadii[2].fX + fRadii[3].fX > rect.width()) { scale = SkMinScalar(scale, SkScalarDiv(rect.width(), fRadii[2].fX + fRadii[3].fX)); } if (fRadii[3].fY + fRadii[0].fY > rect.height()) { scale = SkMinScalar(scale, SkScalarDiv(rect.height(), fRadii[3].fY + fRadii[0].fY)); } if (scale < SK_Scalar1) { for (int i = 0; i < 4; ++i) { fRadii[i].fX = SkScalarMul(fRadii[i].fX, scale); fRadii[i].fY = SkScalarMul(fRadii[i].fY, scale); } } // At this point we're either oval, simple, or complex (not empty or rect) // but we lazily resolve the type to avoid the work if the information // isn't required. fType = (SkRRect::Type) kUnknown_Type; SkDEBUGCODE(this->validate();) }
void generateAAStrokeRectGeometry(void* vertices, size_t offset, size_t vertexStride, int outerVertexNum, int innerVertexNum, GrColor color, const SkRect& devOutside, const SkRect& devOutsideAssist, const SkRect& devInside, bool miterStroke, bool tweakAlphaForCoverage) const { intptr_t verts = reinterpret_cast<intptr_t>(vertices) + offset; // We create vertices for four nested rectangles. There are two ramps from 0 to full // coverage, one on the exterior of the stroke and the other on the interior. // The following pointers refer to the four rects, from outermost to innermost. SkPoint* fan0Pos = reinterpret_cast<SkPoint*>(verts); SkPoint* fan1Pos = reinterpret_cast<SkPoint*>(verts + outerVertexNum * vertexStride); SkPoint* fan2Pos = reinterpret_cast<SkPoint*>(verts + 2 * outerVertexNum * vertexStride); SkPoint* fan3Pos = reinterpret_cast<SkPoint*>(verts + (2 * outerVertexNum + innerVertexNum) * vertexStride); #ifndef SK_IGNORE_THIN_STROKED_RECT_FIX // TODO: this only really works if the X & Y margins are the same all around // the rect (or if they are all >= 1.0). SkScalar inset = SkMinScalar(SK_Scalar1, devOutside.fRight - devInside.fRight); inset = SkMinScalar(inset, devInside.fLeft - devOutside.fLeft); inset = SkMinScalar(inset, devInside.fTop - devOutside.fTop); if (miterStroke) { inset = SK_ScalarHalf * SkMinScalar(inset, devOutside.fBottom - devInside.fBottom); } else { inset = SK_ScalarHalf * SkMinScalar(inset, devOutsideAssist.fBottom - devInside.fBottom); } SkASSERT(inset >= 0); #else SkScalar inset = SK_ScalarHalf; #endif if (miterStroke) { // outermost set_inset_fan(fan0Pos, vertexStride, devOutside, -SK_ScalarHalf, -SK_ScalarHalf); // inner two set_inset_fan(fan1Pos, vertexStride, devOutside, inset, inset); set_inset_fan(fan2Pos, vertexStride, devInside, -inset, -inset); // innermost set_inset_fan(fan3Pos, vertexStride, devInside, SK_ScalarHalf, SK_ScalarHalf); } else { SkPoint* fan0AssistPos = reinterpret_cast<SkPoint*>(verts + 4 * vertexStride); SkPoint* fan1AssistPos = reinterpret_cast<SkPoint*>(verts + (outerVertexNum + 4) * vertexStride); // outermost set_inset_fan(fan0Pos, vertexStride, devOutside, -SK_ScalarHalf, -SK_ScalarHalf); set_inset_fan(fan0AssistPos, vertexStride, devOutsideAssist, -SK_ScalarHalf, -SK_ScalarHalf); // outer one of the inner two set_inset_fan(fan1Pos, vertexStride, devOutside, inset, inset); set_inset_fan(fan1AssistPos, vertexStride, devOutsideAssist, inset, inset); // inner one of the inner two set_inset_fan(fan2Pos, vertexStride, devInside, -inset, -inset); // innermost set_inset_fan(fan3Pos, vertexStride, devInside, SK_ScalarHalf, SK_ScalarHalf); } // Make verts point to vertex color and then set all the color and coverage vertex attrs // values. The outermost rect has 0 coverage verts += sizeof(SkPoint); for (int i = 0; i < outerVertexNum; ++i) { if (tweakAlphaForCoverage) { *reinterpret_cast<GrColor*>(verts + i * vertexStride) = 0; } else { *reinterpret_cast<GrColor*>(verts + i * vertexStride) = color; *reinterpret_cast<float*>(verts + i * vertexStride + sizeof(GrColor)) = 0; } } // scale is the coverage for the the inner two rects. int scale; if (inset < SK_ScalarHalf) { scale = SkScalarFloorToInt(512.0f * inset / (inset + SK_ScalarHalf)); SkASSERT(scale >= 0 && scale <= 255); } else { scale = 0xff; } float innerCoverage = GrNormalizeByteToFloat(scale); GrColor scaledColor = (0xff == scale) ? color : SkAlphaMulQ(color, scale); verts += outerVertexNum * vertexStride; for (int i = 0; i < outerVertexNum + innerVertexNum; ++i) { if (tweakAlphaForCoverage) { *reinterpret_cast<GrColor*>(verts + i * vertexStride) = scaledColor; } else { *reinterpret_cast<GrColor*>(verts + i * vertexStride) = color; *reinterpret_cast<float*>(verts + i * vertexStride + sizeof(GrColor)) = innerCoverage; } } // The innermost rect has 0 coverage verts += (outerVertexNum + innerVertexNum) * vertexStride; for (int i = 0; i < innerVertexNum; ++i) { if (tweakAlphaForCoverage) { *reinterpret_cast<GrColor*>(verts + i * vertexStride) = 0; } else { *reinterpret_cast<GrColor*>(verts + i * vertexStride) = color; *reinterpret_cast<GrColor*>(verts + i * vertexStride + sizeof(GrColor)) = 0; } } }
bool GrDashingEffect::DrawDashLine(const SkPoint pts[2], const GrPaint& paint, const GrStrokeInfo& strokeInfo, GrGpu* gpu, GrDrawTarget* target, const SkMatrix& vm) { if (!can_fast_path_dash(pts, strokeInfo, *target, vm)) { return false; } const SkPathEffect::DashInfo& info = strokeInfo.getDashInfo(); SkPaint::Cap cap = strokeInfo.getStrokeRec().getCap(); SkScalar srcStrokeWidth = strokeInfo.getStrokeRec().getWidth(); // the phase should be normalized to be [0, sum of all intervals) SkASSERT(info.fPhase >= 0 && info.fPhase < info.fIntervals[0] + info.fIntervals[1]); SkScalar srcPhase = info.fPhase; // Rotate the src pts so they are aligned horizontally with pts[0].fX < pts[1].fX SkMatrix srcRotInv; SkPoint ptsRot[2]; if (pts[0].fY != pts[1].fY || pts[0].fX > pts[1].fX) { SkMatrix rotMatrix; align_to_x_axis(pts, &rotMatrix, ptsRot); if(!rotMatrix.invert(&srcRotInv)) { GrPrintf("Failed to create invertible rotation matrix!\n"); return false; } } else { srcRotInv.reset(); memcpy(ptsRot, pts, 2 * sizeof(SkPoint)); } bool useAA = paint.isAntiAlias(); // Scale corrections of intervals and stroke from view matrix SkScalar parallelScale; SkScalar perpScale; calc_dash_scaling(¶llelScale, &perpScale, vm, ptsRot); bool hasCap = SkPaint::kButt_Cap != cap && 0 != srcStrokeWidth; // We always want to at least stroke out half a pixel on each side in device space // so 0.5f / perpScale gives us this min in src space SkScalar halfSrcStroke = SkMaxScalar(srcStrokeWidth * 0.5f, 0.5f / perpScale); SkScalar strokeAdj; if (!hasCap) { strokeAdj = 0.f; } else { strokeAdj = halfSrcStroke; } SkScalar startAdj = 0; SkMatrix combinedMatrix = srcRotInv; combinedMatrix.postConcat(vm); bool lineDone = false; SkRect startRect; bool hasStartRect = false; // If we are using AA, check to see if we are drawing a partial dash at the start. If so // draw it separately here and adjust our start point accordingly if (useAA) { if (srcPhase > 0 && srcPhase < info.fIntervals[0]) { SkPoint startPts[2]; startPts[0] = ptsRot[0]; startPts[1].fY = startPts[0].fY; startPts[1].fX = SkMinScalar(startPts[0].fX + info.fIntervals[0] - srcPhase, ptsRot[1].fX); startRect.set(startPts, 2); startRect.outset(strokeAdj, halfSrcStroke); hasStartRect = true; startAdj = info.fIntervals[0] + info.fIntervals[1] - srcPhase; } } // adjustments for start and end of bounding rect so we only draw dash intervals // contained in the original line segment. startAdj += calc_start_adjustment(info); if (startAdj != 0) { ptsRot[0].fX += startAdj; srcPhase = 0; } SkScalar endingInterval = 0; SkScalar endAdj = calc_end_adjustment(info, ptsRot, srcPhase, &endingInterval); ptsRot[1].fX -= endAdj; if (ptsRot[0].fX >= ptsRot[1].fX) { lineDone = true; } SkRect endRect; bool hasEndRect = false; // If we are using AA, check to see if we are drawing a partial dash at then end. If so // draw it separately here and adjust our end point accordingly if (useAA && !lineDone) { // If we adjusted the end then we will not be drawing a partial dash at the end. // If we didn't adjust the end point then we just need to make sure the ending // dash isn't a full dash if (0 == endAdj && endingInterval != info.fIntervals[0]) { SkPoint endPts[2]; endPts[1] = ptsRot[1]; endPts[0].fY = endPts[1].fY; endPts[0].fX = endPts[1].fX - endingInterval; endRect.set(endPts, 2); endRect.outset(strokeAdj, halfSrcStroke); hasEndRect = true; endAdj = endingInterval + info.fIntervals[1]; ptsRot[1].fX -= endAdj; if (ptsRot[0].fX >= ptsRot[1].fX) { lineDone = true; } } } if (startAdj != 0) { srcPhase = 0; } // Change the dashing info from src space into device space SkScalar devIntervals[2]; devIntervals[0] = info.fIntervals[0] * parallelScale; devIntervals[1] = info.fIntervals[1] * parallelScale; SkScalar devPhase = srcPhase * parallelScale; SkScalar strokeWidth = srcStrokeWidth * perpScale; if ((strokeWidth < 1.f && !useAA) || 0.f == strokeWidth) { strokeWidth = 1.f; } SkScalar halfDevStroke = strokeWidth * 0.5f; if (SkPaint::kSquare_Cap == cap && 0 != srcStrokeWidth) { // add cap to on interveal and remove from off interval devIntervals[0] += strokeWidth; devIntervals[1] -= strokeWidth; } SkScalar startOffset = devIntervals[1] * 0.5f + devPhase; SkScalar bloatX = useAA ? 0.5f / parallelScale : 0.f; SkScalar bloatY = useAA ? 0.5f / perpScale : 0.f; SkScalar devBloat = useAA ? 0.5f : 0.f; GrDrawState* drawState = target->drawState(); if (devIntervals[1] <= 0.f && useAA) { // Case when we end up drawing a solid AA rect // Reset the start rect to draw this single solid rect // but it requires to upload a new intervals uniform so we can mimic // one giant dash ptsRot[0].fX -= hasStartRect ? startAdj : 0; ptsRot[1].fX += hasEndRect ? endAdj : 0; startRect.set(ptsRot, 2); startRect.outset(strokeAdj, halfSrcStroke); hasStartRect = true; hasEndRect = false; lineDone = true; SkPoint devicePts[2]; vm.mapPoints(devicePts, ptsRot, 2); SkScalar lineLength = SkPoint::Distance(devicePts[0], devicePts[1]); if (hasCap) { lineLength += 2.f * halfDevStroke; } devIntervals[0] = lineLength; } if (devIntervals[1] > 0.f || useAA) { SkPathEffect::DashInfo devInfo; devInfo.fPhase = devPhase; devInfo.fCount = 2; devInfo.fIntervals = devIntervals; GrEffectEdgeType edgeType= useAA ? kFillAA_GrEffectEdgeType : kFillBW_GrEffectEdgeType; bool isRoundCap = SkPaint::kRound_Cap == cap; GrDashingEffect::DashCap capType = isRoundCap ? GrDashingEffect::kRound_DashCap : GrDashingEffect::kNonRound_DashCap; drawState->addCoverageEffect( GrDashingEffect::Create(edgeType, devInfo, strokeWidth, capType), 1)->unref(); } // Set up the vertex data for the line and start/end dashes drawState->setVertexAttribs<gDashLineVertexAttribs>(SK_ARRAY_COUNT(gDashLineVertexAttribs)); int totalRectCnt = 0; totalRectCnt += !lineDone ? 1 : 0; totalRectCnt += hasStartRect ? 1 : 0; totalRectCnt += hasEndRect ? 1 : 0; GrDrawTarget::AutoReleaseGeometry geo(target, totalRectCnt * 4, 0); if (!geo.succeeded()) { GrPrintf("Failed to get space for vertices!\n"); return false; } DashLineVertex* verts = reinterpret_cast<DashLineVertex*>(geo.vertices()); int curVIdx = 0; if (SkPaint::kRound_Cap == cap && 0 != srcStrokeWidth) { // need to adjust this for round caps to correctly set the dashPos attrib on vertices startOffset -= halfDevStroke; } // Draw interior part of dashed line if (!lineDone) { SkPoint devicePts[2]; vm.mapPoints(devicePts, ptsRot, 2); SkScalar lineLength = SkPoint::Distance(devicePts[0], devicePts[1]); if (hasCap) { lineLength += 2.f * halfDevStroke; } SkRect bounds; bounds.set(ptsRot[0].fX, ptsRot[0].fY, ptsRot[1].fX, ptsRot[1].fY); bounds.outset(bloatX + strokeAdj, bloatY + halfSrcStroke); setup_dashed_rect(bounds, verts, curVIdx, combinedMatrix, startOffset, devBloat, lineLength, halfDevStroke); curVIdx += 4; } if (hasStartRect) { SkASSERT(useAA); // so that we know bloatX and bloatY have been set startRect.outset(bloatX, bloatY); setup_dashed_rect(startRect, verts, curVIdx, combinedMatrix, startOffset, devBloat, devIntervals[0], halfDevStroke); curVIdx += 4; } if (hasEndRect) { SkASSERT(useAA); // so that we know bloatX and bloatY have been set endRect.outset(bloatX, bloatY); setup_dashed_rect(endRect, verts, curVIdx, combinedMatrix, startOffset, devBloat, devIntervals[0], halfDevStroke); } target->setIndexSourceToBuffer(gpu->getContext()->getQuadIndexBuffer()); target->drawIndexedInstances(kTriangles_GrPrimitiveType, totalRectCnt, 4, 6); target->resetIndexSource(); return true; }
sk_sp<SkSpecialImage> SkMagnifierImageFilter::onFilterImage(SkSpecialImage* source, const Context& ctx, SkIPoint* offset) const { SkIPoint inputOffset = SkIPoint::Make(0, 0); sk_sp<SkSpecialImage> input(this->filterInput(0, source, ctx, &inputOffset)); if (!input) { return nullptr; } const SkIRect inputBounds = SkIRect::MakeXYWH(inputOffset.x(), inputOffset.y(), input->width(), input->height()); SkIRect bounds; if (!this->applyCropRect(ctx, inputBounds, &bounds)) { return nullptr; } SkScalar invInset = fInset > 0 ? SkScalarInvert(fInset) : SK_Scalar1; SkScalar invXZoom = fSrcRect.width() / bounds.width(); SkScalar invYZoom = fSrcRect.height() / bounds.height(); #if SK_SUPPORT_GPU if (source->isTextureBacked()) { GrContext* context = source->getContext(); sk_sp<GrTexture> inputTexture(input->asTextureRef(context)); SkASSERT(inputTexture); offset->fX = bounds.left(); offset->fY = bounds.top(); bounds.offset(-inputOffset); SkScalar yOffset = inputTexture->origin() == kTopLeft_GrSurfaceOrigin ? fSrcRect.y() : inputTexture->height() - fSrcRect.height() * inputTexture->height() / bounds.height() - fSrcRect.y(); int boundsY = inputTexture->origin() == kTopLeft_GrSurfaceOrigin ? bounds.y() : inputTexture->height() - bounds.height(); SkRect effectBounds = SkRect::MakeXYWH( SkIntToScalar(bounds.x()) / inputTexture->width(), SkIntToScalar(boundsY) / inputTexture->height(), SkIntToScalar(inputTexture->width()) / bounds.width(), SkIntToScalar(inputTexture->height()) / bounds.height()); // SRGBTODO: Handle sRGB here sk_sp<GrFragmentProcessor> fp(GrMagnifierEffect::Create( inputTexture.get(), effectBounds, fSrcRect.x() / inputTexture->width(), yOffset / inputTexture->height(), invXZoom, invYZoom, bounds.width() * invInset, bounds.height() * invInset)); if (!fp) { return nullptr; } return DrawWithFP(context, std::move(fp), bounds); } #endif SkBitmap inputBM; if (!input->getROPixels(&inputBM)) { return nullptr; } if ((inputBM.colorType() != kN32_SkColorType) || (fSrcRect.width() >= inputBM.width()) || (fSrcRect.height() >= inputBM.height())) { return nullptr; } SkAutoLockPixels alp(inputBM); SkASSERT(inputBM.getPixels()); if (!inputBM.getPixels() || inputBM.width() <= 0 || inputBM.height() <= 0) { return nullptr; } const SkImageInfo info = SkImageInfo::MakeN32Premul(bounds.width(), bounds.height()); SkBitmap dst; if (!dst.tryAllocPixels(info)) { return nullptr; } SkAutoLockPixels dstLock(dst); SkColor* dptr = dst.getAddr32(0, 0); int dstWidth = dst.width(), dstHeight = dst.height(); for (int y = 0; y < dstHeight; ++y) { for (int x = 0; x < dstWidth; ++x) { SkScalar x_dist = SkMin32(x, dstWidth - x - 1) * invInset; SkScalar y_dist = SkMin32(y, dstHeight - y - 1) * invInset; SkScalar weight = 0; static const SkScalar kScalar2 = SkScalar(2); // To create a smooth curve at the corners, we need to work on // a square twice the size of the inset. if (x_dist < kScalar2 && y_dist < kScalar2) { x_dist = kScalar2 - x_dist; y_dist = kScalar2 - y_dist; SkScalar dist = SkScalarSqrt(SkScalarSquare(x_dist) + SkScalarSquare(y_dist)); dist = SkMaxScalar(kScalar2 - dist, 0); weight = SkMinScalar(SkScalarSquare(dist), SK_Scalar1); } else { SkScalar sqDist = SkMinScalar(SkScalarSquare(x_dist), SkScalarSquare(y_dist)); weight = SkMinScalar(sqDist, SK_Scalar1); } SkScalar x_interp = SkScalarMul(weight, (fSrcRect.x() + x * invXZoom)) + (SK_Scalar1 - weight) * x; SkScalar y_interp = SkScalarMul(weight, (fSrcRect.y() + y * invYZoom)) + (SK_Scalar1 - weight) * y; int x_val = SkTPin(bounds.x() + SkScalarFloorToInt(x_interp), 0, inputBM.width() - 1); int y_val = SkTPin(bounds.y() + SkScalarFloorToInt(y_interp), 0, inputBM.height() - 1); *dptr = *inputBM.getAddr32(x_val, y_val); dptr++; } } offset->fX = bounds.left(); offset->fY = bounds.top(); return SkSpecialImage::MakeFromRaster(SkIRect::MakeWH(bounds.width(), bounds.height()), dst); }
bool SkBlurImageFilter::onFilterImage(Proxy* proxy, const SkBitmap& source, const Context& ctx, SkBitmap* dst, SkIPoint* offset) const { SkBitmap src = source; SkIPoint srcOffset = SkIPoint::Make(0, 0); if (getInput(0) && !getInput(0)->filterImage(proxy, source, ctx, &src, &srcOffset)) { return false; } if (src.colorType() != kN32_SkColorType) { return false; } SkIRect srcBounds, dstBounds; if (!this->applyCropRect(ctx, proxy, src, &srcOffset, &srcBounds, &src)) { return false; } SkAutoLockPixels alp(src); if (!src.getPixels()) { return false; } if (!dst->allocPixels(src.info().makeWH(srcBounds.width(), srcBounds.height()))) { return false; } dst->getBounds(&dstBounds); SkVector sigma = SkVector::Make(fSigma.width(), fSigma.height()); ctx.ctm().mapVectors(&sigma, 1); sigma.fX = SkMinScalar(sigma.fX, MAX_SIGMA); sigma.fY = SkMinScalar(sigma.fY, MAX_SIGMA); int kernelSizeX, kernelSizeX3, lowOffsetX, highOffsetX; int kernelSizeY, kernelSizeY3, lowOffsetY, highOffsetY; getBox3Params(sigma.x(), &kernelSizeX, &kernelSizeX3, &lowOffsetX, &highOffsetX); getBox3Params(sigma.y(), &kernelSizeY, &kernelSizeY3, &lowOffsetY, &highOffsetY); if (kernelSizeX < 0 || kernelSizeY < 0) { return false; } if (kernelSizeX == 0 && kernelSizeY == 0) { src.copyTo(dst, dst->colorType()); offset->fX = srcBounds.fLeft; offset->fY = srcBounds.fTop; return true; } SkBitmap temp; if (!temp.allocPixels(dst->info())) { return false; } offset->fX = srcBounds.fLeft; offset->fY = srcBounds.fTop; srcBounds.offset(-srcOffset); const SkPMColor* s = src.getAddr32(srcBounds.left(), srcBounds.top()); SkPMColor* t = temp.getAddr32(0, 0); SkPMColor* d = dst->getAddr32(0, 0); int w = dstBounds.width(), h = dstBounds.height(); int sw = src.rowBytesAsPixels(); SkBoxBlurProc boxBlurX, boxBlurY, boxBlurXY, boxBlurYX; if (!SkBoxBlurGetPlatformProcs(&boxBlurX, &boxBlurY, &boxBlurXY, &boxBlurYX)) { boxBlurX = boxBlur<kX, kX>; boxBlurY = boxBlur<kY, kY>; boxBlurXY = boxBlur<kX, kY>; boxBlurYX = boxBlur<kY, kX>; } if (kernelSizeX > 0 && kernelSizeY > 0) { boxBlurX(s, sw, t, kernelSizeX, lowOffsetX, highOffsetX, w, h); boxBlurX(t, w, d, kernelSizeX, highOffsetX, lowOffsetX, w, h); boxBlurXY(d, w, t, kernelSizeX3, highOffsetX, highOffsetX, w, h); boxBlurX(t, h, d, kernelSizeY, lowOffsetY, highOffsetY, h, w); boxBlurX(d, h, t, kernelSizeY, highOffsetY, lowOffsetY, h, w); boxBlurXY(t, h, d, kernelSizeY3, highOffsetY, highOffsetY, h, w); } else if (kernelSizeX > 0) { boxBlurX(s, sw, d, kernelSizeX, lowOffsetX, highOffsetX, w, h); boxBlurX(d, w, t, kernelSizeX, highOffsetX, lowOffsetX, w, h); boxBlurX(t, w, d, kernelSizeX3, highOffsetX, highOffsetX, w, h); } else if (kernelSizeY > 0) { boxBlurYX(s, sw, d, kernelSizeY, lowOffsetY, highOffsetY, h, w); boxBlurX(d, h, t, kernelSizeY, highOffsetY, lowOffsetY, h, w); boxBlurXY(t, h, d, kernelSizeY3, highOffsetY, highOffsetY, h, w); } return true; }
bool SkXRayCrossesMonotonicCubic(const SkXRay& pt, const SkPoint cubic[4]) { // Find the minimum and maximum y of the extrema, which are the // first and last points since this cubic is monotonic SkScalar min_y = SkMinScalar(cubic[0].fY, cubic[3].fY); SkScalar max_y = SkMaxScalar(cubic[0].fY, cubic[3].fY); if (pt.fY == cubic[0].fY || pt.fY < min_y || pt.fY > max_y) { // The query line definitely does not cross the curve return false; } SkScalar min_x = SkMinScalar( SkMinScalar( SkMinScalar(cubic[0].fX, cubic[1].fX), cubic[2].fX), cubic[3].fX); if (pt.fX < min_x) { // The query line definitely crosses the curve return true; } SkScalar max_x = SkMaxScalar( SkMaxScalar( SkMaxScalar(cubic[0].fX, cubic[1].fX), cubic[2].fX), cubic[3].fX); if (pt.fX > max_x) { // The query line definitely does not cross the curve return false; } // Do a binary search to find the parameter value which makes y as // close as possible to the query point. See whether the query // line's origin is to the left of the associated x coordinate. // kMaxIter is chosen as the number of mantissa bits for a float, // since there's no way we are going to get more precision by // iterating more times than that. const int kMaxIter = 23; SkPoint eval; int iter = 0; SkScalar upper_t; SkScalar lower_t; // Need to invert direction of t parameter if cubic goes up // instead of down if (cubic[3].fY > cubic[0].fY) { upper_t = SK_Scalar1; lower_t = SkFloatToScalar(0); } else { upper_t = SkFloatToScalar(0); lower_t = SK_Scalar1; } do { SkScalar t = SkScalarAve(upper_t, lower_t); SkEvalCubicAt(cubic, t, &eval, NULL, NULL); if (pt.fY > eval.fY) { lower_t = t; } else { upper_t = t; } } while (++iter < kMaxIter && !SkScalarNearlyZero(eval.fY - pt.fY)); if (pt.fX <= eval.fX) { return true; } return false; }
void SkDisplayMath::executeFunction(SkDisplayable* target, int index, SkTDArray<SkScriptValue>& parameters, SkDisplayTypes type, SkScriptValue* scriptValue) { if (scriptValue == NULL) return; SkASSERT(target == this); SkScriptValue* array = parameters.begin(); SkScriptValue* end = parameters.end(); SkScalar input = parameters[0].fOperand.fScalar; SkScalar scalarResult; switch (index) { case SK_FUNCTION(abs): scalarResult = SkScalarAbs(input); break; case SK_FUNCTION(acos): scalarResult = SkScalarACos(input); break; case SK_FUNCTION(asin): scalarResult = SkScalarASin(input); break; case SK_FUNCTION(atan): scalarResult = SkScalarATan2(input, SK_Scalar1); break; case SK_FUNCTION(atan2): scalarResult = SkScalarATan2(input, parameters[1].fOperand.fScalar); break; case SK_FUNCTION(ceil): scalarResult = SkIntToScalar(SkScalarCeil(input)); break; case SK_FUNCTION(cos): scalarResult = SkScalarCos(input); break; case SK_FUNCTION(exp): scalarResult = SkScalarExp(input); break; case SK_FUNCTION(floor): scalarResult = SkIntToScalar(SkScalarFloor(input)); break; case SK_FUNCTION(log): scalarResult = SkScalarLog(input); break; case SK_FUNCTION(max): scalarResult = -SK_ScalarMax; while (array < end) { scalarResult = SkMaxScalar(scalarResult, array->fOperand.fScalar); array++; } break; case SK_FUNCTION(min): scalarResult = SK_ScalarMax; while (array < end) { scalarResult = SkMinScalar(scalarResult, array->fOperand.fScalar); array++; } break; case SK_FUNCTION(pow): // not the greatest -- but use x^y = e^(y * ln(x)) scalarResult = SkScalarLog(input); scalarResult = SkScalarMul(parameters[1].fOperand.fScalar, scalarResult); scalarResult = SkScalarExp(scalarResult); break; case SK_FUNCTION(random): scalarResult = fRandom.nextUScalar1(); break; case SK_FUNCTION(round): scalarResult = SkIntToScalar(SkScalarRound(input)); break; case SK_FUNCTION(sin): scalarResult = SkScalarSin(input); break; case SK_FUNCTION(sqrt): { SkASSERT(parameters.count() == 1); SkASSERT(type == SkType_Float); scalarResult = SkScalarSqrt(input); } break; case SK_FUNCTION(tan): scalarResult = SkScalarTan(input); break; default: SkASSERT(0); scalarResult = SK_ScalarNaN; } scriptValue->fOperand.fScalar = scalarResult; scriptValue->fType = SkType_Float; }
void GrAARectRenderer::geometryFillAARect(GrDrawTarget* target, GrDrawState* drawState, GrColor color, const SkRect& rect, const SkMatrix& combinedMatrix, const SkRect& devRect) { GrDrawState::AutoRestoreEffects are(drawState); CoverageAttribType type; SkAutoTUnref<const GrGeometryProcessor> gp(create_rect_gp(*drawState, color, &type)); size_t vertexStride = gp->getVertexStride(); GrDrawTarget::AutoReleaseGeometry geo(target, 8, vertexStride, 0); if (!geo.succeeded()) { SkDebugf("Failed to get space for vertices!\n"); return; } if (NULL == fAAFillRectIndexBuffer) { fAAFillRectIndexBuffer = fGpu->createInstancedIndexBuffer(gFillAARectIdx, kIndicesPerAAFillRect, kNumAAFillRectsInIndexBuffer, kVertsPerAAFillRect); } GrIndexBuffer* indexBuffer = fAAFillRectIndexBuffer; if (NULL == indexBuffer) { SkDebugf("Failed to create index buffer!\n"); return; } intptr_t verts = reinterpret_cast<intptr_t>(geo.vertices()); SkPoint* fan0Pos = reinterpret_cast<SkPoint*>(verts); SkPoint* fan1Pos = reinterpret_cast<SkPoint*>(verts + 4 * vertexStride); SkScalar inset = SkMinScalar(devRect.width(), SK_Scalar1); inset = SK_ScalarHalf * SkMinScalar(inset, devRect.height()); if (combinedMatrix.rectStaysRect()) { // Temporarily #if'ed out. We don't want to pass in the devRect but // right now it is computed in GrContext::apply_aa_to_rect and we don't // want to throw away the work #if 0 SkRect devRect; combinedMatrix.mapRect(&devRect, rect); #endif set_inset_fan(fan0Pos, vertexStride, devRect, -SK_ScalarHalf, -SK_ScalarHalf); set_inset_fan(fan1Pos, vertexStride, devRect, inset, inset); } else { // compute transformed (1, 0) and (0, 1) vectors SkVector vec[2] = { { combinedMatrix[SkMatrix::kMScaleX], combinedMatrix[SkMatrix::kMSkewY] }, { combinedMatrix[SkMatrix::kMSkewX], combinedMatrix[SkMatrix::kMScaleY] } }; vec[0].normalize(); vec[0].scale(SK_ScalarHalf); vec[1].normalize(); vec[1].scale(SK_ScalarHalf); // create the rotated rect fan0Pos->setRectFan(rect.fLeft, rect.fTop, rect.fRight, rect.fBottom, vertexStride); combinedMatrix.mapPointsWithStride(fan0Pos, vertexStride, 4); // Now create the inset points and then outset the original // rotated points // TL *((SkPoint*)((intptr_t)fan1Pos + 0 * vertexStride)) = *((SkPoint*)((intptr_t)fan0Pos + 0 * vertexStride)) + vec[0] + vec[1]; *((SkPoint*)((intptr_t)fan0Pos + 0 * vertexStride)) -= vec[0] + vec[1]; // BL *((SkPoint*)((intptr_t)fan1Pos + 1 * vertexStride)) = *((SkPoint*)((intptr_t)fan0Pos + 1 * vertexStride)) + vec[0] - vec[1]; *((SkPoint*)((intptr_t)fan0Pos + 1 * vertexStride)) -= vec[0] - vec[1]; // BR *((SkPoint*)((intptr_t)fan1Pos + 2 * vertexStride)) = *((SkPoint*)((intptr_t)fan0Pos + 2 * vertexStride)) - vec[0] - vec[1]; *((SkPoint*)((intptr_t)fan0Pos + 2 * vertexStride)) += vec[0] + vec[1]; // TR *((SkPoint*)((intptr_t)fan1Pos + 3 * vertexStride)) = *((SkPoint*)((intptr_t)fan0Pos + 3 * vertexStride)) - vec[0] + vec[1]; *((SkPoint*)((intptr_t)fan0Pos + 3 * vertexStride)) += vec[0] - vec[1]; } // Make verts point to vertex color and then set all the color and coverage vertex attrs values. verts += sizeof(SkPoint); for (int i = 0; i < 4; ++i) { if (kUseCoverage_CoverageAttribType == type) { *reinterpret_cast<GrColor*>(verts + i * vertexStride) = color; *reinterpret_cast<float*>(verts + i * vertexStride + sizeof(GrColor)) = 0; } else { *reinterpret_cast<GrColor*>(verts + i * vertexStride) = 0; } } int scale; if (inset < SK_ScalarHalf) { scale = SkScalarFloorToInt(512.0f * inset / (inset + SK_ScalarHalf)); SkASSERT(scale >= 0 && scale <= 255); } else { scale = 0xff; } verts += 4 * vertexStride; float innerCoverage = GrNormalizeByteToFloat(scale); GrColor scaledColor = (0xff == scale) ? color : SkAlphaMulQ(color, scale); for (int i = 0; i < 4; ++i) { if (kUseCoverage_CoverageAttribType == type) { *reinterpret_cast<GrColor*>(verts + i * vertexStride) = color; *reinterpret_cast<float*>(verts + i * vertexStride + sizeof(GrColor)) = innerCoverage; } else { *reinterpret_cast<GrColor*>(verts + i * vertexStride) = scaledColor; } } target->setIndexSourceToBuffer(indexBuffer); target->drawIndexedInstances(drawState, gp, kTriangles_GrPrimitiveType, 1, kVertsPerAAFillRect, kIndicesPerAAFillRect); target->resetIndexSource(); }
void generateAAFillRectGeometry(void* vertices, size_t offset, size_t vertexStride, GrColor color, const SkMatrix& viewMatrix, const SkRect& rect, const SkRect& devRect, bool tweakAlphaForCoverage) const { intptr_t verts = reinterpret_cast<intptr_t>(vertices) + offset; SkPoint* fan0Pos = reinterpret_cast<SkPoint*>(verts); SkPoint* fan1Pos = reinterpret_cast<SkPoint*>(verts + 4 * vertexStride); SkScalar inset = SkMinScalar(devRect.width(), SK_Scalar1); inset = SK_ScalarHalf * SkMinScalar(inset, devRect.height()); if (viewMatrix.rectStaysRect()) { set_inset_fan(fan0Pos, vertexStride, devRect, -SK_ScalarHalf, -SK_ScalarHalf); set_inset_fan(fan1Pos, vertexStride, devRect, inset, inset); } else { // compute transformed (1, 0) and (0, 1) vectors SkVector vec[2] = { { viewMatrix[SkMatrix::kMScaleX], viewMatrix[SkMatrix::kMSkewY] }, { viewMatrix[SkMatrix::kMSkewX], viewMatrix[SkMatrix::kMScaleY] } }; vec[0].normalize(); vec[0].scale(SK_ScalarHalf); vec[1].normalize(); vec[1].scale(SK_ScalarHalf); // create the rotated rect fan0Pos->setRectFan(rect.fLeft, rect.fTop, rect.fRight, rect.fBottom, vertexStride); viewMatrix.mapPointsWithStride(fan0Pos, vertexStride, 4); // Now create the inset points and then outset the original // rotated points // TL *((SkPoint*)((intptr_t)fan1Pos + 0 * vertexStride)) = *((SkPoint*)((intptr_t)fan0Pos + 0 * vertexStride)) + vec[0] + vec[1]; *((SkPoint*)((intptr_t)fan0Pos + 0 * vertexStride)) -= vec[0] + vec[1]; // BL *((SkPoint*)((intptr_t)fan1Pos + 1 * vertexStride)) = *((SkPoint*)((intptr_t)fan0Pos + 1 * vertexStride)) + vec[0] - vec[1]; *((SkPoint*)((intptr_t)fan0Pos + 1 * vertexStride)) -= vec[0] - vec[1]; // BR *((SkPoint*)((intptr_t)fan1Pos + 2 * vertexStride)) = *((SkPoint*)((intptr_t)fan0Pos + 2 * vertexStride)) - vec[0] - vec[1]; *((SkPoint*)((intptr_t)fan0Pos + 2 * vertexStride)) += vec[0] + vec[1]; // TR *((SkPoint*)((intptr_t)fan1Pos + 3 * vertexStride)) = *((SkPoint*)((intptr_t)fan0Pos + 3 * vertexStride)) - vec[0] + vec[1]; *((SkPoint*)((intptr_t)fan0Pos + 3 * vertexStride)) += vec[0] - vec[1]; } // Make verts point to vertex color and then set all the color and coverage vertex attrs // values. verts += sizeof(SkPoint); for (int i = 0; i < 4; ++i) { if (tweakAlphaForCoverage) { *reinterpret_cast<GrColor*>(verts + i * vertexStride) = 0; } else { *reinterpret_cast<GrColor*>(verts + i * vertexStride) = color; *reinterpret_cast<float*>(verts + i * vertexStride + sizeof(GrColor)) = 0; } } int scale; if (inset < SK_ScalarHalf) { scale = SkScalarFloorToInt(512.0f * inset / (inset + SK_ScalarHalf)); SkASSERT(scale >= 0 && scale <= 255); } else { scale = 0xff; } verts += 4 * vertexStride; float innerCoverage = GrNormalizeByteToFloat(scale); GrColor scaledColor = (0xff == scale) ? color : SkAlphaMulQ(color, scale); for (int i = 0; i < 4; ++i) { if (tweakAlphaForCoverage) { *reinterpret_cast<GrColor*>(verts + i * vertexStride) = scaledColor; } else { *reinterpret_cast<GrColor*>(verts + i * vertexStride) = color; *reinterpret_cast<float*>(verts + i * vertexStride + sizeof(GrColor)) = innerCoverage; } } }
bool SkMagnifierImageFilter::onFilterImage(Proxy*, const SkBitmap& src, const Context&, SkBitmap* dst, SkIPoint* offset) const { if ((src.colorType() != kN32_SkColorType) || (fSrcRect.width() >= src.width()) || (fSrcRect.height() >= src.height())) { return false; } SkAutoLockPixels alp(src); SkASSERT(src.getPixels()); if (!src.getPixels() || src.width() <= 0 || src.height() <= 0) { return false; } if (!dst->tryAllocPixels(src.info())) { return false; } SkScalar inv_inset = fInset > 0 ? SkScalarInvert(fInset) : SK_Scalar1; SkScalar inv_x_zoom = fSrcRect.width() / src.width(); SkScalar inv_y_zoom = fSrcRect.height() / src.height(); SkColor* sptr = src.getAddr32(0, 0); SkColor* dptr = dst->getAddr32(0, 0); int width = src.width(), height = src.height(); for (int y = 0; y < height; ++y) { for (int x = 0; x < width; ++x) { SkScalar x_dist = SkMin32(x, width - x - 1) * inv_inset; SkScalar y_dist = SkMin32(y, height - y - 1) * inv_inset; SkScalar weight = 0; static const SkScalar kScalar2 = SkScalar(2); // To create a smooth curve at the corners, we need to work on // a square twice the size of the inset. if (x_dist < kScalar2 && y_dist < kScalar2) { x_dist = kScalar2 - x_dist; y_dist = kScalar2 - y_dist; SkScalar dist = SkScalarSqrt(SkScalarSquare(x_dist) + SkScalarSquare(y_dist)); dist = SkMaxScalar(kScalar2 - dist, 0); weight = SkMinScalar(SkScalarSquare(dist), SK_Scalar1); } else { SkScalar sqDist = SkMinScalar(SkScalarSquare(x_dist), SkScalarSquare(y_dist)); weight = SkMinScalar(sqDist, SK_Scalar1); } SkScalar x_interp = SkScalarMul(weight, (fSrcRect.x() + x * inv_x_zoom)) + (SK_Scalar1 - weight) * x; SkScalar y_interp = SkScalarMul(weight, (fSrcRect.y() + y * inv_y_zoom)) + (SK_Scalar1 - weight) * y; int x_val = SkPin32(SkScalarFloorToInt(x_interp), 0, width - 1); int y_val = SkPin32(SkScalarFloorToInt(y_interp), 0, height - 1); *dptr = sptr[y_val * width + x_val]; dptr++; } } return true; }
// Currently asPoints is more restrictive then it needs to be. In the future // we need to: // allow kRound_Cap capping (could allow rotations in the matrix with this) // allow paths to be returned bool SkDashPathEffect::asPoints(PointData* results, const SkPath& src, const SkStrokeRec& rec, const SkMatrix& matrix, const SkRect* cullRect) const { // width < 0 -> fill && width == 0 -> hairline so requiring width > 0 rules both out if (fInitialDashLength < 0 || 0 >= rec.getWidth()) { return false; } // TODO: this next test could be eased up. We could allow any number of // intervals as long as all the ons match and all the offs match. // Additionally, they do not necessarily need to be integers. // We cannot allow arbitrary intervals since we want the returned points // to be uniformly sized. if (fCount != 2 || !SkScalarNearlyEqual(fIntervals[0], fIntervals[1]) || !SkScalarIsInt(fIntervals[0]) || !SkScalarIsInt(fIntervals[1])) { return false; } SkPoint pts[2]; if (!src.isLine(pts)) { return false; } // TODO: this test could be eased up to allow circles if (SkPaint::kButt_Cap != rec.getCap()) { return false; } // TODO: this test could be eased up for circles. Rotations could be allowed. if (!matrix.rectStaysRect()) { return false; } // See if the line can be limited to something plausible. if (!cull_line(pts, rec, matrix, cullRect, fIntervalLength)) { return false; } SkScalar length = SkPoint::Distance(pts[1], pts[0]); SkVector tangent = pts[1] - pts[0]; if (tangent.isZero()) { return false; } tangent.scale(SkScalarInvert(length)); // TODO: make this test for horizontal & vertical lines more robust bool isXAxis = true; if (SkScalarNearlyEqual(SK_Scalar1, tangent.fX) || SkScalarNearlyEqual(-SK_Scalar1, tangent.fX)) { results->fSize.set(SkScalarHalf(fIntervals[0]), SkScalarHalf(rec.getWidth())); } else if (SkScalarNearlyEqual(SK_Scalar1, tangent.fY) || SkScalarNearlyEqual(-SK_Scalar1, tangent.fY)) { results->fSize.set(SkScalarHalf(rec.getWidth()), SkScalarHalf(fIntervals[0])); isXAxis = false; } else if (SkPaint::kRound_Cap != rec.getCap()) { // Angled lines don't have axis-aligned boxes. return false; } if (results) { results->fFlags = 0; SkScalar clampedInitialDashLength = SkMinScalar(length, fInitialDashLength); if (SkPaint::kRound_Cap == rec.getCap()) { results->fFlags |= PointData::kCircles_PointFlag; } results->fNumPoints = 0; SkScalar len2 = length; if (clampedInitialDashLength > 0 || 0 == fInitialDashIndex) { SkASSERT(len2 >= clampedInitialDashLength); if (0 == fInitialDashIndex) { if (clampedInitialDashLength > 0) { if (clampedInitialDashLength >= fIntervals[0]) { ++results->fNumPoints; // partial first dash } len2 -= clampedInitialDashLength; } len2 -= fIntervals[1]; // also skip first space if (len2 < 0) { len2 = 0; } } else { len2 -= clampedInitialDashLength; // skip initial partial empty } } int numMidPoints = SkScalarFloorToInt(len2 / fIntervalLength); results->fNumPoints += numMidPoints; len2 -= numMidPoints * fIntervalLength; bool partialLast = false; if (len2 > 0) { if (len2 < fIntervals[0]) { partialLast = true; } else { ++numMidPoints; ++results->fNumPoints; } } results->fPoints = new SkPoint[results->fNumPoints]; SkScalar distance = 0; int curPt = 0; if (clampedInitialDashLength > 0 || 0 == fInitialDashIndex) { SkASSERT(clampedInitialDashLength <= length); if (0 == fInitialDashIndex) { if (clampedInitialDashLength > 0) { // partial first block SkASSERT(SkPaint::kRound_Cap != rec.getCap()); // can't handle partial circles SkScalar x = pts[0].fX + SkScalarMul(tangent.fX, SkScalarHalf(clampedInitialDashLength)); SkScalar y = pts[0].fY + SkScalarMul(tangent.fY, SkScalarHalf(clampedInitialDashLength)); SkScalar halfWidth, halfHeight; if (isXAxis) { halfWidth = SkScalarHalf(clampedInitialDashLength); halfHeight = SkScalarHalf(rec.getWidth()); } else { halfWidth = SkScalarHalf(rec.getWidth()); halfHeight = SkScalarHalf(clampedInitialDashLength); } if (clampedInitialDashLength < fIntervals[0]) { // This one will not be like the others results->fFirst.addRect(x - halfWidth, y - halfHeight, x + halfWidth, y + halfHeight); } else { SkASSERT(curPt < results->fNumPoints); results->fPoints[curPt].set(x, y); ++curPt; } distance += clampedInitialDashLength; } distance += fIntervals[1]; // skip over the next blank block too } else { distance += clampedInitialDashLength; } } if (0 != numMidPoints) { distance += SkScalarHalf(fIntervals[0]); for (int i = 0; i < numMidPoints; ++i) { SkScalar x = pts[0].fX + SkScalarMul(tangent.fX, distance); SkScalar y = pts[0].fY + SkScalarMul(tangent.fY, distance); SkASSERT(curPt < results->fNumPoints); results->fPoints[curPt].set(x, y); ++curPt; distance += fIntervalLength; } distance -= SkScalarHalf(fIntervals[0]); } if (partialLast) { // partial final block SkASSERT(SkPaint::kRound_Cap != rec.getCap()); // can't handle partial circles SkScalar temp = length - distance; SkASSERT(temp < fIntervals[0]); SkScalar x = pts[0].fX + SkScalarMul(tangent.fX, distance + SkScalarHalf(temp)); SkScalar y = pts[0].fY + SkScalarMul(tangent.fY, distance + SkScalarHalf(temp)); SkScalar halfWidth, halfHeight; if (isXAxis) { halfWidth = SkScalarHalf(temp); halfHeight = SkScalarHalf(rec.getWidth()); } else { halfWidth = SkScalarHalf(rec.getWidth()); halfHeight = SkScalarHalf(temp); } results->fLast.addRect(x - halfWidth, y - halfHeight, x + halfWidth, y + halfHeight); } SkASSERT(curPt == results->fNumPoints); } return true; }
void GrAARectRenderer::geometryStrokeAARect(GrDrawTarget* target, GrDrawState* drawState, GrColor color, const SkRect& devOutside, const SkRect& devOutsideAssist, const SkRect& devInside, bool miterStroke) { GrDrawState::AutoRestoreEffects are(drawState); CoverageAttribType type; SkAutoTUnref<const GrGeometryProcessor> gp(create_rect_gp(*drawState, color, &type)); int innerVertexNum = 4; int outerVertexNum = miterStroke ? 4 : 8; int totalVertexNum = (outerVertexNum + innerVertexNum) * 2; size_t vstride = gp->getVertexStride(); GrDrawTarget::AutoReleaseGeometry geo(target, totalVertexNum, vstride, 0); if (!geo.succeeded()) { SkDebugf("Failed to get space for vertices!\n"); return; } GrIndexBuffer* indexBuffer = this->aaStrokeRectIndexBuffer(miterStroke); if (NULL == indexBuffer) { SkDebugf("Failed to create index buffer!\n"); return; } intptr_t verts = reinterpret_cast<intptr_t>(geo.vertices()); // We create vertices for four nested rectangles. There are two ramps from 0 to full // coverage, one on the exterior of the stroke and the other on the interior. // The following pointers refer to the four rects, from outermost to innermost. SkPoint* fan0Pos = reinterpret_cast<SkPoint*>(verts); SkPoint* fan1Pos = reinterpret_cast<SkPoint*>(verts + outerVertexNum * vstride); SkPoint* fan2Pos = reinterpret_cast<SkPoint*>(verts + 2 * outerVertexNum * vstride); SkPoint* fan3Pos = reinterpret_cast<SkPoint*>(verts + (2 * outerVertexNum + innerVertexNum) * vstride); #ifndef SK_IGNORE_THIN_STROKED_RECT_FIX // TODO: this only really works if the X & Y margins are the same all around // the rect (or if they are all >= 1.0). SkScalar inset = SkMinScalar(SK_Scalar1, devOutside.fRight - devInside.fRight); inset = SkMinScalar(inset, devInside.fLeft - devOutside.fLeft); inset = SkMinScalar(inset, devInside.fTop - devOutside.fTop); if (miterStroke) { inset = SK_ScalarHalf * SkMinScalar(inset, devOutside.fBottom - devInside.fBottom); } else { inset = SK_ScalarHalf * SkMinScalar(inset, devOutsideAssist.fBottom - devInside.fBottom); } SkASSERT(inset >= 0); #else SkScalar inset = SK_ScalarHalf; #endif if (miterStroke) { // outermost set_inset_fan(fan0Pos, vstride, devOutside, -SK_ScalarHalf, -SK_ScalarHalf); // inner two set_inset_fan(fan1Pos, vstride, devOutside, inset, inset); set_inset_fan(fan2Pos, vstride, devInside, -inset, -inset); // innermost set_inset_fan(fan3Pos, vstride, devInside, SK_ScalarHalf, SK_ScalarHalf); } else { SkPoint* fan0AssistPos = reinterpret_cast<SkPoint*>(verts + 4 * vstride); SkPoint* fan1AssistPos = reinterpret_cast<SkPoint*>(verts + (outerVertexNum + 4) * vstride); // outermost set_inset_fan(fan0Pos, vstride, devOutside, -SK_ScalarHalf, -SK_ScalarHalf); set_inset_fan(fan0AssistPos, vstride, devOutsideAssist, -SK_ScalarHalf, -SK_ScalarHalf); // outer one of the inner two set_inset_fan(fan1Pos, vstride, devOutside, inset, inset); set_inset_fan(fan1AssistPos, vstride, devOutsideAssist, inset, inset); // inner one of the inner two set_inset_fan(fan2Pos, vstride, devInside, -inset, -inset); // innermost set_inset_fan(fan3Pos, vstride, devInside, SK_ScalarHalf, SK_ScalarHalf); } // Make verts point to vertex color and then set all the color and coverage vertex attrs values. // The outermost rect has 0 coverage verts += sizeof(SkPoint); for (int i = 0; i < outerVertexNum; ++i) { if (kUseCoverage_CoverageAttribType == type) { *reinterpret_cast<GrColor*>(verts + i * vstride) = color; *reinterpret_cast<float*>(verts + i * vstride + sizeof(GrColor)) = 0; } else { *reinterpret_cast<GrColor*>(verts + i * vstride) = 0; } } // scale is the coverage for the the inner two rects. int scale; if (inset < SK_ScalarHalf) { scale = SkScalarFloorToInt(512.0f * inset / (inset + SK_ScalarHalf)); SkASSERT(scale >= 0 && scale <= 255); } else { scale = 0xff; } float innerCoverage = GrNormalizeByteToFloat(scale); GrColor scaledColor = (0xff == scale) ? color : SkAlphaMulQ(color, scale); verts += outerVertexNum * vstride; for (int i = 0; i < outerVertexNum + innerVertexNum; ++i) { if (kUseCoverage_CoverageAttribType == type) { *reinterpret_cast<GrColor*>(verts + i * vstride) = color; *reinterpret_cast<float*>(verts + i * vstride + sizeof(GrColor)) = innerCoverage; } else { *reinterpret_cast<GrColor*>(verts + i * vstride) = scaledColor; } } // The innermost rect has 0 coverage verts += (outerVertexNum + innerVertexNum) * vstride; for (int i = 0; i < innerVertexNum; ++i) { if (kUseCoverage_CoverageAttribType == type) { *reinterpret_cast<GrColor*>(verts + i * vstride) = color; *reinterpret_cast<GrColor*>(verts + i * vstride + sizeof(GrColor)) = 0; } else { *reinterpret_cast<GrColor*>(verts + i * vstride) = 0; } } target->setIndexSourceToBuffer(indexBuffer); target->drawIndexedInstances(drawState, gp, kTriangles_GrPrimitiveType, 1, totalVertexNum, aa_stroke_rect_index_count(miterStroke)); target->resetIndexSource(); }