SkColor SkHSVToColor(U8CPU a, const SkScalar hsv[3]) { SkASSERT(hsv); SkScalar s = SkScalarPin(hsv[1], 0, 1); SkScalar v = SkScalarPin(hsv[2], 0, 1); U8CPU v_byte = SkScalarRoundToInt(v * 255); if (SkScalarNearlyZero(s)) { // shade of gray return SkColorSetARGB(a, v_byte, v_byte, v_byte); } SkScalar hx = (hsv[0] < 0 || hsv[0] >= SkIntToScalar(360)) ? 0 : hsv[0]/60; SkScalar w = SkScalarFloorToScalar(hx); SkScalar f = hx - w; unsigned p = SkScalarRoundToInt((SK_Scalar1 - s) * v * 255); unsigned q = SkScalarRoundToInt((SK_Scalar1 - (s * f)) * v * 255); unsigned t = SkScalarRoundToInt((SK_Scalar1 - (s * (SK_Scalar1 - f))) * v * 255); unsigned r, g, b; SkASSERT((unsigned)(w) < 6); switch ((unsigned)(w)) { case 0: r = v_byte; g = t; b = p; break; case 1: r = q; g = v_byte; b = p; break; case 2: r = p; g = v_byte; b = t; break; case 3: r = p; g = q; b = v_byte; break; case 4: r = t; g = p; b = v_byte; break; default: r = v_byte; g = p; b = q; break; } return SkColorSetARGB(a, r, g, b); }
/** * Our antialiasing currently has a granularity of 1/4 of a pixel along each * axis. Thus we can treat an axis coordinate as an integer if it differs * from its nearest int by < half of that value (1.8 in this case). */ static bool nearly_integral(SkScalar x) { static const SkScalar domain = SK_Scalar1 / 4; static const SkScalar halfDomain = domain / 2; x += halfDomain; return x - SkScalarFloorToScalar(x) < domain; }
// Only called once. Could be part of the constructor. void stitch() { SkScalar tileWidth = SkIntToScalar(fTileSize.width()); SkScalar tileHeight = SkIntToScalar(fTileSize.height()); SkASSERT(tileWidth > 0 && tileHeight > 0); // When stitching tiled turbulence, the frequencies must be adjusted // so that the tile borders will be continuous. if (fBaseFrequency.fX) { SkScalar lowFrequencx = SkScalarFloorToScalar(tileWidth * fBaseFrequency.fX) / tileWidth; SkScalar highFrequencx = SkScalarCeilToScalar(tileWidth * fBaseFrequency.fX) / tileWidth; // BaseFrequency should be non-negative according to the standard. if (SkScalarDiv(fBaseFrequency.fX, lowFrequencx) < SkScalarDiv(highFrequencx, fBaseFrequency.fX)) { fBaseFrequency.fX = lowFrequencx; } else { fBaseFrequency.fX = highFrequencx; } } if (fBaseFrequency.fY) { SkScalar lowFrequency = SkScalarFloorToScalar(tileHeight * fBaseFrequency.fY) / tileHeight; SkScalar highFrequency = SkScalarCeilToScalar(tileHeight * fBaseFrequency.fY) / tileHeight; if (SkScalarDiv(fBaseFrequency.fY, lowFrequency) < SkScalarDiv(highFrequency, fBaseFrequency.fY)) { fBaseFrequency.fY = lowFrequency; } else { fBaseFrequency.fY = highFrequency; } } // Set up TurbulenceInitial stitch values. fStitchDataInit.fWidth = SkScalarRoundToInt(tileWidth * fBaseFrequency.fX); fStitchDataInit.fWrapX = kPerlinNoise + fStitchDataInit.fWidth; fStitchDataInit.fHeight = SkScalarRoundToInt(tileHeight * fBaseFrequency.fY); fStitchDataInit.fWrapY = kPerlinNoise + fStitchDataInit.fHeight; }
static SkScalar calc_end_adjustment(const SkPathEffect::DashInfo& info, const SkPoint pts[2], SkScalar phase, SkScalar* endingInt) { if (pts[1].fX <= pts[0].fX) { return 0; } SkScalar srcIntervalLen = info.fIntervals[0] + info.fIntervals[1]; SkScalar totalLen = pts[1].fX - pts[0].fX; SkScalar temp = SkScalarDiv(totalLen, srcIntervalLen); SkScalar numFullIntervals = SkScalarFloorToScalar(temp); *endingInt = totalLen - numFullIntervals * srcIntervalLen + phase; temp = SkScalarDiv(*endingInt, srcIntervalLen); *endingInt = *endingInt - SkScalarFloorToScalar(temp) * srcIntervalLen; if (0 == *endingInt) { *endingInt = srcIntervalLen; } if (*endingInt > info.fIntervals[0]) { if (0 == info.fIntervals[0]) { *endingInt -= 0.01f; // make sure we capture the last zero size pnt (used if has caps) } return *endingInt - info.fIntervals[0]; } return 0; }
void onPrepareDraws(Target* target) const override { int instanceCount = fGeoData.count(); SkMatrix invert; if (this->usesLocalCoords() && !this->viewMatrix().invert(&invert)) { SkDebugf("Could not invert viewmatrix\n"); return; } // Setup GrGeometryProcessors SkAutoTUnref<GrPLSGeometryProcessor> triangleProcessor( PLSAATriangleEffect::Create(invert, this->usesLocalCoords())); SkAutoTUnref<GrPLSGeometryProcessor> quadProcessor( PLSQuadEdgeEffect::Create(invert, this->usesLocalCoords())); GrResourceProvider* rp = target->resourceProvider(); for (int i = 0; i < instanceCount; ++i) { const Geometry& args = fGeoData[i]; SkRect bounds = args.fPath.getBounds(); args.fViewMatrix.mapRect(&bounds); bounds.fLeft = SkScalarFloorToScalar(bounds.fLeft); bounds.fTop = SkScalarFloorToScalar(bounds.fTop); bounds.fRight = SkScalarCeilToScalar(bounds.fRight); bounds.fBottom = SkScalarCeilToScalar(bounds.fBottom); triangleProcessor->setBounds(bounds); quadProcessor->setBounds(bounds); // We use the fact that SkPath::transform path does subdivision based on // perspective. Otherwise, we apply the view matrix when copying to the // segment representation. const SkMatrix* viewMatrix = &args.fViewMatrix; // We avoid initializing the path unless we have to const SkPath* pathPtr = &args.fPath; SkTLazy<SkPath> tmpPath; if (viewMatrix->hasPerspective()) { SkPath* tmpPathPtr = tmpPath.init(*pathPtr); tmpPathPtr->setIsVolatile(true); tmpPathPtr->transform(*viewMatrix); viewMatrix = &SkMatrix::I(); pathPtr = tmpPathPtr; } GrVertices grVertices; PLSVertices triVertices; PLSVertices quadVertices; if (!get_geometry(*pathPtr, *viewMatrix, triVertices, quadVertices, rp, bounds)) { continue; } if (triVertices.count()) { const GrVertexBuffer* triVertexBuffer; int firstTriVertex; size_t triStride = triangleProcessor->getVertexStride(); PLSVertex* triVerts = reinterpret_cast<PLSVertex*>(target->makeVertexSpace( triStride, triVertices.count(), &triVertexBuffer, &firstTriVertex)); if (!triVerts) { SkDebugf("Could not allocate vertices\n"); return; } for (int i = 0; i < triVertices.count(); ++i) { triVerts[i] = triVertices[i]; } grVertices.init(kTriangles_GrPrimitiveType, triVertexBuffer, firstTriVertex, triVertices.count()); target->initDraw(triangleProcessor, this->pipeline()); target->draw(grVertices); } if (quadVertices.count()) { const GrVertexBuffer* quadVertexBuffer; int firstQuadVertex; size_t quadStride = quadProcessor->getVertexStride(); PLSVertex* quadVerts = reinterpret_cast<PLSVertex*>(target->makeVertexSpace( quadStride, quadVertices.count(), &quadVertexBuffer, &firstQuadVertex)); if (!quadVerts) { SkDebugf("Could not allocate vertices\n"); return; } for (int i = 0; i < quadVertices.count(); ++i) { quadVerts[i] = quadVertices[i]; } grVertices.init(kTriangles_GrPrimitiveType, quadVertexBuffer, firstQuadVertex, quadVertices.count()); target->initDraw(quadProcessor, this->pipeline()); target->draw(grVertices); } SkAutoTUnref<GrGeometryProcessor> finishProcessor( PLSFinishEffect::Create(this->color(), pathPtr->getFillType() == SkPath::FillType::kEvenOdd_FillType, invert, this->usesLocalCoords())); const GrVertexBuffer* rectVertexBuffer; size_t finishStride = finishProcessor->getVertexStride(); int firstRectVertex; static const int kRectVertexCount = 6; SkPoint* rectVerts = reinterpret_cast<SkPoint*>(target->makeVertexSpace( finishStride, kRectVertexCount, &rectVertexBuffer, &firstRectVertex)); if (!rectVerts) { SkDebugf("Could not allocate vertices\n"); return; } rectVerts[0] = { bounds.fLeft, bounds.fTop }; rectVerts[1] = { bounds.fLeft, bounds.fBottom }; rectVerts[2] = { bounds.fRight, bounds.fBottom }; rectVerts[3] = { bounds.fLeft, bounds.fTop }; rectVerts[4] = { bounds.fRight, bounds.fTop }; rectVerts[5] = { bounds.fRight, bounds.fBottom }; grVertices.init(kTriangles_GrPrimitiveType, rectVertexBuffer, firstRectVertex, kRectVertexCount); target->initDraw(finishProcessor, this->pipeline()); target->draw(grVertices); } }
// TODO(egouriou): Take advantage of periods in the convolution. // Practical resizing filters are periodic outside of the border area. // For Lanczos, a scaling by a (reduced) factor of p/q (q pixels in the // source become p pixels in the destination) will have a period of p. // A nice consequence is a period of 1 when downscaling by an integral // factor. Downscaling from typical display resolutions is also bound // to produce interesting periods as those are chosen to have multiple // small factors. // Small periods reduce computational load and improve cache usage if // the coefficients can be shared. For periods of 1 we can consider // loading the factors only once outside the borders. void SkResizeFilter::computeFilters(int srcSize, float destSubsetLo, float destSubsetSize, float scale, SkConvolutionFilter1D* output, const SkConvolutionProcs& convolveProcs) { float destSubsetHi = destSubsetLo + destSubsetSize; // [lo, hi) // When we're doing a magnification, the scale will be larger than one. This // means the destination pixels are much smaller than the source pixels, and // that the range covered by the filter won't necessarily cover any source // pixel boundaries. Therefore, we use these clamped values (max of 1) for // some computations. float clampedScale = SkTMin(1.0f, scale); // This is how many source pixels from the center we need to count // to support the filtering function. float srcSupport = fBitmapFilter->width() / clampedScale; float invScale = 1.0f / scale; SkSTArray<64, float, true> filterValuesArray; SkSTArray<64, SkConvolutionFilter1D::ConvolutionFixed, true> fixedFilterValuesArray; // Loop over all pixels in the output range. We will generate one set of // filter values for each one. Those values will tell us how to blend the // source pixels to compute the destination pixel. // This is the pixel in the source directly under the pixel in the dest. // Note that we base computations on the "center" of the pixels. To see // why, observe that the destination pixel at coordinates (0, 0) in a 5.0x // downscale should "cover" the pixels around the pixel with *its center* // at coordinates (2.5, 2.5) in the source, not those around (0, 0). // Hence we need to scale coordinates (0.5, 0.5), not (0, 0). destSubsetLo = SkScalarFloorToScalar(destSubsetLo); destSubsetHi = SkScalarCeilToScalar(destSubsetHi); float srcPixel = (destSubsetLo + 0.5f) * invScale; int destLimit = SkScalarTruncToInt(destSubsetHi - destSubsetLo); output->reserveAdditional(destLimit, SkScalarCeilToInt(destLimit * srcSupport * 2)); for (int destI = 0; destI < destLimit; srcPixel += invScale, destI++) { // Compute the (inclusive) range of source pixels the filter covers. float srcBegin = SkTMax(0.f, SkScalarFloorToScalar(srcPixel - srcSupport)); float srcEnd = SkTMin(srcSize - 1.f, SkScalarCeilToScalar(srcPixel + srcSupport)); // Compute the unnormalized filter value at each location of the source // it covers. // Sum of the filter values for normalizing. // Distance from the center of the filter, this is the filter coordinate // in source space. We also need to consider the center of the pixel // when comparing distance against 'srcPixel'. In the 5x downscale // example used above the distance from the center of the filter to // the pixel with coordinates (2, 2) should be 0, because its center // is at (2.5, 2.5). float destFilterDist = (srcBegin + 0.5f - srcPixel) * clampedScale; int filterCount = SkScalarTruncToInt(srcEnd - srcBegin) + 1; if (filterCount <= 0) { // true when srcSize is equal to srcPixel - srcSupport; this may be a bug return; } filterValuesArray.reset(filterCount); float filterSum = fBitmapFilter->evaluate_n(destFilterDist, clampedScale, filterCount, filterValuesArray.begin()); // The filter must be normalized so that we don't affect the brightness of // the image. Convert to normalized fixed point. int fixedSum = 0; fixedFilterValuesArray.reset(filterCount); const float* filterValues = filterValuesArray.begin(); SkConvolutionFilter1D::ConvolutionFixed* fixedFilterValues = fixedFilterValuesArray.begin(); float invFilterSum = 1 / filterSum; for (int fixedI = 0; fixedI < filterCount; fixedI++) { int curFixed = SkConvolutionFilter1D::FloatToFixed(filterValues[fixedI] * invFilterSum); fixedSum += curFixed; fixedFilterValues[fixedI] = SkToS16(curFixed); } SkASSERT(fixedSum <= 0x7FFF); // The conversion to fixed point will leave some rounding errors, which // we add back in to avoid affecting the brightness of the image. We // arbitrarily add this to the center of the filter array (this won't always // be the center of the filter function since it could get clipped on the // edges, but it doesn't matter enough to worry about that case). int leftovers = SkConvolutionFilter1D::FloatToFixed(1) - fixedSum; fixedFilterValues[filterCount / 2] += leftovers; // Now it's ready to go. output->AddFilter(SkScalarFloorToInt(srcBegin), fixedFilterValues, filterCount); } if (convolveProcs.fApplySIMDPadding) { convolveProcs.fApplySIMDPadding(output); } }
bool SkBicubicImageFilter::onFilterImage(Proxy* proxy, const SkBitmap& source, const SkMatrix& matrix, SkBitmap* result, SkIPoint* loc) { SkBitmap src = source; if (getInput(0) && !getInput(0)->filterImage(proxy, source, matrix, &src, loc)) { return false; } if (src.config() != SkBitmap::kARGB_8888_Config) { return false; } SkAutoLockPixels alp(src); if (!src.getPixels()) { return false; } SkRect dstRect = SkRect::MakeWH(SkScalarMul(SkIntToScalar(src.width()), fScale.fWidth), SkScalarMul(SkIntToScalar(src.height()), fScale.fHeight)); SkIRect dstIRect; dstRect.roundOut(&dstIRect); result->setConfig(src.config(), dstIRect.width(), dstIRect.height()); result->allocPixels(); if (!result->getPixels()) { return false; } SkRect srcRect; src.getBounds(&srcRect); SkMatrix inverse; inverse.setRectToRect(dstRect, srcRect, SkMatrix::kFill_ScaleToFit); inverse.postTranslate(SkFloatToScalar(-0.5f), SkFloatToScalar(-0.5f)); for (int y = dstIRect.fTop; y < dstIRect.fBottom; ++y) { SkPMColor* dptr = result->getAddr32(dstIRect.fLeft, y); for (int x = dstIRect.fLeft; x < dstIRect.fRight; ++x) { SkPoint srcPt, dstPt = SkPoint::Make(SkIntToScalar(x), SkIntToScalar(y)); inverse.mapPoints(&srcPt, &dstPt, 1); SkScalar fractx = srcPt.fX - SkScalarFloorToScalar(srcPt.fX); SkScalar fracty = srcPt.fY - SkScalarFloorToScalar(srcPt.fY); int sx = SkScalarFloorToInt(srcPt.fX); int sy = SkScalarFloorToInt(srcPt.fY); int x0 = SkClampMax(sx - 1, src.width() - 1); int x1 = SkClampMax(sx , src.width() - 1); int x2 = SkClampMax(sx + 1, src.width() - 1); int x3 = SkClampMax(sx + 2, src.width() - 1); int y0 = SkClampMax(sy - 1, src.height() - 1); int y1 = SkClampMax(sy , src.height() - 1); int y2 = SkClampMax(sy + 1, src.height() - 1); int y3 = SkClampMax(sy + 2, src.height() - 1); SkPMColor s00 = *src.getAddr32(x0, y0); SkPMColor s10 = *src.getAddr32(x1, y0); SkPMColor s20 = *src.getAddr32(x2, y0); SkPMColor s30 = *src.getAddr32(x3, y0); SkPMColor s0 = cubicBlend(fCoefficients, fractx, s00, s10, s20, s30); SkPMColor s01 = *src.getAddr32(x0, y1); SkPMColor s11 = *src.getAddr32(x1, y1); SkPMColor s21 = *src.getAddr32(x2, y1); SkPMColor s31 = *src.getAddr32(x3, y1); SkPMColor s1 = cubicBlend(fCoefficients, fractx, s01, s11, s21, s31); SkPMColor s02 = *src.getAddr32(x0, y2); SkPMColor s12 = *src.getAddr32(x1, y2); SkPMColor s22 = *src.getAddr32(x2, y2); SkPMColor s32 = *src.getAddr32(x3, y2); SkPMColor s2 = cubicBlend(fCoefficients, fractx, s02, s12, s22, s32); SkPMColor s03 = *src.getAddr32(x0, y3); SkPMColor s13 = *src.getAddr32(x1, y3); SkPMColor s23 = *src.getAddr32(x2, y3); SkPMColor s33 = *src.getAddr32(x3, y3); SkPMColor s3 = cubicBlend(fCoefficients, fractx, s03, s13, s23, s33); *dptr++ = cubicBlend(fCoefficients, fracty, s0, s1, s2, s3); } } return true; }
void onDraw(int loops, SkCanvas* canvas) override { SkRandom scaleRand; SkRandom transRand; SkRandom rotRand; int width, height; if (fUseAtlas) { width = kAtlasCellWidth; height = kAtlasCellHeight; } else { width = kCheckerboardWidth; height = kCheckerboardHeight; } SkPaint clearPaint; clearPaint.setColor(0xFF000000); clearPaint.setAntiAlias(true); SkISize size = canvas->getDeviceSize(); SkScalar maxTransX, maxTransY; if (kScale_Type == fType) { maxTransX = size.fWidth - (1.5f * width); maxTransY = size.fHeight - (1.5f * height); } else if (kTranslate_Type == fType) { maxTransX = SkIntToScalar(size.fWidth - width); maxTransY = SkIntToScalar(size.fHeight - height); } else { SkASSERT(kRotate_Type == fType); // Yes, some rotations will be off the top and left sides maxTransX = size.fWidth - SK_ScalarSqrt2 * height; maxTransY = size.fHeight - SK_ScalarSqrt2 * height; } SkMatrix mat; SkRect dst = { 0, 0, SkIntToScalar(width), SkIntToScalar(height) }; SkRect clearRect = { -1.0f, -1.0f, width+1.0f, height+1.0f }; SkPoint verts[4] = { // for drawVertices path { 0, 0 }, { 0, SkIntToScalar(height) }, { SkIntToScalar(width), SkIntToScalar(height) }, { SkIntToScalar(width), 0 } }; uint16_t indices[6] = { 0, 1, 2, 0, 2, 3 }; SkPaint p; p.setColor(0xFF000000); p.setFilterQuality(kLow_SkFilterQuality); SkPaint p2; // for drawVertices path p2.setColor(0xFF000000); p2.setFilterQuality(kLow_SkFilterQuality); p2.setShader(SkShader::MakeBitmapShader(fAtlas, SkShader::kClamp_TileMode, SkShader::kClamp_TileMode)); for (int i = 0; i < loops; ++i, ++fNumSaved) { if (0 == i % kNumBeforeClear) { if (kPartial_Clear == fClear) { for (int j = 0; j < fNumSaved; ++j) { canvas->setMatrix(SkMatrix::I()); mat.setTranslate(fSaved[j][0], fSaved[j][1]); if (kScale_Type == fType) { mat.preScale(fSaved[j][2], fSaved[j][2]); } else if (kRotate_Type == fType) { mat.preRotate(fSaved[j][2]); } canvas->concat(mat); canvas->drawRect(clearRect, clearPaint); } } else { canvas->clear(0xFF000000); } fNumSaved = 0; } SkASSERT(fNumSaved < kNumBeforeClear); canvas->setMatrix(SkMatrix::I()); fSaved[fNumSaved][0] = transRand.nextRangeScalar(0.0f, maxTransX); fSaved[fNumSaved][1] = transRand.nextRangeScalar(0.0f, maxTransY); if (fAligned) { // make the translations integer aligned fSaved[fNumSaved][0] = SkScalarFloorToScalar(fSaved[fNumSaved][0]); fSaved[fNumSaved][1] = SkScalarFloorToScalar(fSaved[fNumSaved][1]); } mat.setTranslate(fSaved[fNumSaved][0], fSaved[fNumSaved][1]); if (kScale_Type == fType) { fSaved[fNumSaved][2] = scaleRand.nextRangeScalar(0.5f, 1.5f); mat.preScale(fSaved[fNumSaved][2], fSaved[fNumSaved][2]); } else if (kRotate_Type == fType) { fSaved[fNumSaved][2] = rotRand.nextRangeScalar(0.0f, 360.0f); mat.preRotate(fSaved[fNumSaved][2]); } canvas->concat(mat); if (fUseAtlas) { const int curCell = i % (kNumAtlasedX * kNumAtlasedY); SkIRect src = fAtlasRects[curCell % (kNumAtlasedX)][curCell / (kNumAtlasedX)]; if (fUseDrawVertices) { SkPoint uvs[4] = { { SkIntToScalar(src.fLeft), SkIntToScalar(src.fBottom) }, { SkIntToScalar(src.fLeft), SkIntToScalar(src.fTop) }, { SkIntToScalar(src.fRight), SkIntToScalar(src.fTop) }, { SkIntToScalar(src.fRight), SkIntToScalar(src.fBottom) }, }; canvas->drawVertices(SkCanvas::kTriangles_VertexMode, 4, verts, uvs, nullptr, nullptr, indices, 6, p2); } else { canvas->drawBitmapRect(fAtlas, src, dst, &p, SkCanvas::kFast_SrcRectConstraint); } } else { canvas->drawBitmapRect(fCheckerboard, dst, &p); } } }
void SkClipStack::Element::updateBoundAndGenID(const Element* prior) { // We set this first here but we may overwrite it later if we determine that the clip is // either wide-open or empty. fGenID = GetNextGenID(); // First, optimistically update the current Element's bound information // with the current clip's bound fIsIntersectionOfRects = false; switch (fType) { case kRect_Type: fFiniteBound = this->getRect(); fFiniteBoundType = kNormal_BoundsType; if (SkRegion::kReplace_Op == fOp || (SkRegion::kIntersect_Op == fOp && nullptr == prior) || (SkRegion::kIntersect_Op == fOp && prior->fIsIntersectionOfRects && prior->rectRectIntersectAllowed(this->getRect(), fDoAA))) { fIsIntersectionOfRects = true; } break; case kRRect_Type: fFiniteBound = fRRect.getBounds(); fFiniteBoundType = kNormal_BoundsType; break; case kPath_Type: fFiniteBound = fPath.get()->getBounds(); if (fPath.get()->isInverseFillType()) { fFiniteBoundType = kInsideOut_BoundsType; } else { fFiniteBoundType = kNormal_BoundsType; } break; case kEmpty_Type: SkDEBUGFAIL("We shouldn't get here with an empty element."); break; } if (!fDoAA) { fFiniteBound.set(SkScalarFloorToScalar(fFiniteBound.fLeft+0.45f), SkScalarRoundToScalar(fFiniteBound.fTop), SkScalarRoundToScalar(fFiniteBound.fRight), SkScalarRoundToScalar(fFiniteBound.fBottom)); } // Now determine the previous Element's bound information taking into // account that there may be no previous clip SkRect prevFinite; SkClipStack::BoundsType prevType; if (nullptr == prior) { // no prior clip means the entire plane is writable prevFinite.setEmpty(); // there are no pixels that cannot be drawn to prevType = kInsideOut_BoundsType; } else { prevFinite = prior->fFiniteBound; prevType = prior->fFiniteBoundType; } FillCombo combination = kPrev_Cur_FillCombo; if (kInsideOut_BoundsType == fFiniteBoundType) { combination = (FillCombo) (combination | 0x01); } if (kInsideOut_BoundsType == prevType) { combination = (FillCombo) (combination | 0x02); } SkASSERT(kInvPrev_InvCur_FillCombo == combination || kInvPrev_Cur_FillCombo == combination || kPrev_InvCur_FillCombo == combination || kPrev_Cur_FillCombo == combination); // Now integrate with clip with the prior clips switch (fOp) { case SkRegion::kDifference_Op: this->combineBoundsDiff(combination, prevFinite); break; case SkRegion::kXOR_Op: this->combineBoundsXOR(combination, prevFinite); break; case SkRegion::kUnion_Op: this->combineBoundsUnion(combination, prevFinite); break; case SkRegion::kIntersect_Op: this->combineBoundsIntersection(combination, prevFinite); break; case SkRegion::kReverseDifference_Op: this->combineBoundsRevDiff(combination, prevFinite); break; case SkRegion::kReplace_Op: // Replace just ignores everything prior // The current clip's bound information is already filled in // so nothing to do break; default: SkDebugf("SkRegion::Op error\n"); SkASSERT(0); break; } }
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 = SkScalarCeilToScalar(input); break; case SK_FUNCTION(cos): scalarResult = SkScalarCos(input); break; case SK_FUNCTION(exp): scalarResult = SkScalarExp(input); break; case SK_FUNCTION(floor): scalarResult = SkScalarFloorToScalar(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 = SkScalarRoundToScalar(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; }