// Only handles lines for now. If returns true, dstPath is the new (smaller) // path. If returns false, then dstPath parameter is ignored. static bool cull_path(const SkPath& srcPath, const SkStrokeRec& rec, const SkRect* cullRect, SkScalar intervalLength, SkPath* dstPath) { if (NULL == cullRect) { return false; } SkPoint pts[2]; if (!srcPath.isLine(pts)) { return false; } SkRect bounds = *cullRect; outset_for_stroke(&bounds, rec); SkScalar dx = pts[1].x() - pts[0].x(); SkScalar dy = pts[1].y() - pts[0].y(); // just do horizontal lines for now (lazy) if (dy) { return false; } SkScalar minX = pts[0].fX; SkScalar maxX = pts[1].fX; if (maxX < bounds.fLeft || minX > bounds.fRight) { return false; } if (dx < 0) { SkTSwap(minX, maxX); } // Now we actually perform the chop, removing the excess to the left and // right of the bounds (keeping our new line "in phase" with the dash, // hence the (mod intervalLength). if (minX < bounds.fLeft) { minX = bounds.fLeft - SkScalarMod(bounds.fLeft - minX, intervalLength); } if (maxX > bounds.fRight) { maxX = bounds.fRight + SkScalarMod(maxX - bounds.fRight, intervalLength); } SkASSERT(maxX >= minX); if (dx < 0) { SkTSwap(minX, maxX); } pts[0].fX = minX; pts[1].fX = maxX; dstPath->moveTo(pts[0]); dstPath->lineTo(pts[1]); return true; }
// first pass, add missing T values // second pass, determine winding values of overlaps void SkOpContour::addCoincidentPoints() { int count = fCoincidences.count(); for (int index = 0; index < count; ++index) { SkCoincidence& coincidence = fCoincidences[index]; int thisIndex = coincidence.fSegments[0]; SkOpSegment& thisOne = fSegments[thisIndex]; SkOpContour* otherContour = coincidence.fOther; int otherIndex = coincidence.fSegments[1]; SkOpSegment& other = otherContour->fSegments[otherIndex]; if ((thisOne.done() || other.done()) && thisOne.complete() && other.complete()) { // OPTIMIZATION: remove from array continue; } #if DEBUG_CONCIDENT thisOne.debugShowTs(); other.debugShowTs(); #endif double startT = coincidence.fTs[0][0]; double endT = coincidence.fTs[0][1]; bool startSwapped, oStartSwapped, cancelers; if ((cancelers = startSwapped = startT > endT)) { SkTSwap(startT, endT); } SkASSERT(!approximately_negative(endT - startT)); double oStartT = coincidence.fTs[1][0]; double oEndT = coincidence.fTs[1][1]; if ((oStartSwapped = oStartT > oEndT)) { SkTSwap(oStartT, oEndT); cancelers ^= true; } SkASSERT(!approximately_negative(oEndT - oStartT)); if (cancelers) { // make sure startT and endT have t entries if (startT > 0 || oEndT < 1 || thisOne.isMissing(startT) || other.isMissing(oEndT)) { thisOne.addTPair(startT, &other, oEndT, true, coincidence.fPts[startSwapped]); } if (oStartT > 0 || endT < 1 || thisOne.isMissing(endT) || other.isMissing(oStartT)) { other.addTPair(oStartT, &thisOne, endT, true, coincidence.fPts[oStartSwapped]); } } else { if (startT > 0 || oStartT > 0 || thisOne.isMissing(startT) || other.isMissing(oStartT)) { thisOne.addTPair(startT, &other, oStartT, true, coincidence.fPts[startSwapped]); } if (endT < 1 || oEndT < 1 || thisOne.isMissing(endT) || other.isMissing(oEndT)) { other.addTPair(oEndT, &thisOne, endT, true, coincidence.fPts[!oStartSwapped]); } } #if DEBUG_CONCIDENT thisOne.debugShowTs(); other.debugShowTs(); #endif } }
int SkEdge::setLine(const SkPoint& p0, const SkPoint& p1, const SkIRect* clip, int shift) { SkFDot6 x0, y0, x1, y1; { #ifdef SK_RASTERIZE_EVEN_ROUNDING x0 = SkScalarRoundToFDot6(p0.fX, shift); y0 = SkScalarRoundToFDot6(p0.fY, shift); x1 = SkScalarRoundToFDot6(p1.fX, shift); y1 = SkScalarRoundToFDot6(p1.fY, shift); #else float scale = float(1 << (shift + 6)); x0 = int(p0.fX * scale); y0 = int(p0.fY * scale); x1 = int(p1.fX * scale); y1 = int(p1.fY * scale); #endif } int winding = 1; if (y0 > y1) { SkTSwap(x0, x1); SkTSwap(y0, y1); winding = -1; } int top = SkFDot6Round(y0); int bot = SkFDot6Round(y1); // are we a zero-height line? if (top == bot) { return 0; } // are we completely above or below the clip? if (clip && (top >= clip->fBottom || bot <= clip->fTop)) { return 0; } SkFixed slope = SkFDot6Div(x1 - x0, y1 - y0); const SkFDot6 dy = SkEdge_Compute_DY(top, y0); fX = SkFDot6ToFixed(x0 + SkFixedMul(slope, dy)); // + SK_Fixed1/2 fDX = slope; fFirstY = top; fLastY = bot - 1; fCurveCount = 0; fWinding = SkToS8(winding); fCurveShift = 0; if (clip) { this->chopLineWithClip(*clip); } return 1; }
int SkEdge::setLine(const SkPoint& p0, const SkPoint& p1, const SkIRect* clip, int shift) { SkFDot6 x0, y0, x1, y1; { #ifdef SK_SCALAR_IS_FLOAT float scale = float(1 << (shift + 6)); x0 = int(p0.fX * scale); y0 = int(p0.fY * scale); x1 = int(p1.fX * scale); y1 = int(p1.fY * scale); #else shift = 10 - shift; x0 = p0.fX >> shift; y0 = p0.fY >> shift; x1 = p1.fX >> shift; y1 = p1.fY >> shift; #endif } int winding = 1; if (y0 > y1) { SkTSwap(x0, x1); SkTSwap(y0, y1); winding = -1; } int top = SkFDot6Round(y0); int bot = SkFDot6Round(y1); // are we a zero-height line? if (top == bot) { return 0; } // are we completely above or below the clip? if (NULL != clip && (top >= clip->fBottom || bot <= clip->fTop)) { return 0; } SkFixed slope = SkFDot6Div(x1 - x0, y1 - y0); fX = SkFDot6ToFixed(x0 + SkFixedMul(slope, (32 - y0) & 63)); // + SK_Fixed1/2 fDX = slope; fFirstY = top; fLastY = bot - 1; fCurveCount = 0; fWinding = SkToS8(winding); fCurveShift = 0; if (clip) { this->chopLineWithClip(*clip); } return 1; }
int SkPDFCatalog::assignObjNum(SkPDFObject* obj) { int pos = findObjectIndex(obj); // If this assert fails, it means you probably forgot to add an object // to the resource list. SkASSERT(pos >= 0); uint32_t currentIndex = pos; if (fCatalog[currentIndex].fObjNumAssigned) { return currentIndex + 1; } // First assignment. if (fNextFirstPageObjNum == 0) { fNextFirstPageObjNum = fCatalog.count() - fFirstPageCount + 1; } uint32_t objNum; if (fCatalog[currentIndex].fOnFirstPage) { objNum = fNextFirstPageObjNum; fNextFirstPageObjNum++; } else { objNum = fNextObjNum; fNextObjNum++; } // When we assign an object an object number, we put it in that array // offset (minus 1 because object number 0 is reserved). SkASSERT(!fCatalog[objNum - 1].fObjNumAssigned); if (objNum - 1 != currentIndex) { SkTSwap(fCatalog[objNum - 1], fCatalog[currentIndex]); } fCatalog[objNum - 1].fObjNumAssigned = true; return objNum; }
static int winding_line(const SkPoint pts[], SkScalar x, SkScalar y) { SkScalar x0 = pts[0].fX; SkScalar y0 = pts[0].fY; SkScalar x1 = pts[1].fX; SkScalar y1 = pts[1].fY; SkScalar dy = y1 - y0; int dir = 1; if (y0 > y1) { SkTSwap(y0, y1); dir = -1; } if (y < y0 || y >= y1) { return 0; } SkScalar cross = SkScalarMul(x1 - x0, y - pts[0].fY) - SkScalarMul(dy, x - pts[0].fX); if (SkScalarSignAsInt(cross) == dir) { dir = 0; } return dir; }
void GrGpuGLShaders::flushTextureDomain(int s) { const GrGLint& uni = fProgramData->fUniLocations.fStages[s].fTexDomUni; const GrDrawState& drawState = this->getDrawState(); if (GrGLProgram::kUnusedUniform != uni) { const GrRect &texDom = drawState.getSampler(s).getTextureDomain(); if (((1 << s) & fDirtyFlags.fTextureChangedMask) || fProgramData->fTextureDomain[s] != texDom) { fProgramData->fTextureDomain[s] = texDom; float values[4] = { GrScalarToFloat(texDom.left()), GrScalarToFloat(texDom.top()), GrScalarToFloat(texDom.right()), GrScalarToFloat(texDom.bottom()) }; const GrGLTexture* texture = static_cast<const GrGLTexture*>(drawState.getTexture(s)); GrGLTexture::Orientation orientation = texture->orientation(); // vertical flip if necessary if (GrGLTexture::kBottomUp_Orientation == orientation) { values[1] = 1.0f - values[1]; values[3] = 1.0f - values[3]; // The top and bottom were just flipped, so correct the ordering // of elements so that values = (l, t, r, b). SkTSwap(values[1], values[3]); } GL_CALL(Uniform4fv(uni, 1, values)); } } }
// Returns true if a ray from (0,0) to (x1,y1) is coincident with a ray (0,0) to (x2,y2) // OPTIMIZE: a specialty routine could speed this up -- may not be called very often though bool SkDLine::NearRay(double x1, double y1, double x2, double y2) { double denom1 = x1 * x1 + y1 * y1; double denom2 = x2 * x2 + y2 * y2; SkDLine line = {{{0, 0}, {x1, y1}}}; SkDPoint pt = {x2, y2}; if (denom2 > denom1) { SkTSwap(line[1], pt); } return line.nearRay(pt); }
void SkMatrix44::transpose() { SkTSwap(fMat[0][1], fMat[1][0]); SkTSwap(fMat[0][2], fMat[2][0]); SkTSwap(fMat[0][3], fMat[3][0]); SkTSwap(fMat[1][2], fMat[2][1]); SkTSwap(fMat[1][3], fMat[3][1]); SkTSwap(fMat[2][3], fMat[3][2]); }
static int vertical_coincident(const SkDLine& line, double x) { double min = line[0].fX; double max = line[1].fX; if (min > max) { SkTSwap(min, max); } if (!precisely_between(min, x, max)) { return 0; } if (AlmostEqualUlps(min, max)) { return 2; } return 1; }
static int horizontal_coincident(const SkDLine& line, double y) { double min = line[0].fY; double max = line[1].fY; if (min > max) { SkTSwap(min, max); } if (min > y || max < y) { return 0; } if (AlmostEqualUlps(min, max) && max - min < fabs(line[0].fX - line[1].fX)) { return 2; } return 1; }
template <typename T> T pin_unsorted(T value, T limit0, T limit1) { if (limit1 < limit0) { SkTSwap(limit0, limit1); } // now the limits are sorted SkASSERT(limit0 <= limit1); if (value < limit0) { value = limit0; } else if (value > limit1) { value = limit1; } return value; }
int intersect(const Cubic& c, Intersections& i) { // check to see if x or y end points are the extrema. Are other quick rejects possible? if (ends_are_extrema_in_x_or_y(c)) { return false; } (void) intersect3(c, c, i); if (i.used() > 0) { SkASSERT(i.used() == 1); if (i.fT[0][0] > i.fT[1][0]) { SkTSwap(i.fT[0][0], i.fT[1][0]); } } return i.used(); }
void SkLinearGradient:: LinearGradient4fContext::shadeSpanInternal(int x, int y, dstType dst[], int count, float bias0, float bias1) const { SkPoint pt; fDstToPosProc(fDstToPos, x + SK_ScalarHalf, y + SK_ScalarHalf, &pt); const SkScalar fx = pinFx<tileMode>(pt.x()); const SkScalar dx = fDstToPos.getScaleX(); LinearIntervalProcessor<dstType, premul, tileMode> proc(fIntervals->begin(), fIntervals->end() - 1, this->findInterval(fx), fx, dx, SkScalarNearlyZero(dx * count)); Sk4f bias4f0(bias0), bias4f1(bias1); while (count > 0) { // What we really want here is SkTPin(advance, 1, count) // but that's a significant perf hit for >> stops; investigate. const int n = SkScalarTruncToInt( SkTMin<SkScalar>(proc.currentAdvance() + 1, SkIntToScalar(count))); // The current interval advance can be +inf (e.g. when reaching // the clamp mode end intervals) - when that happens, we expect to // a) consume all remaining count in one swoop // b) return a zero color gradient SkASSERT(SkScalarIsFinite(proc.currentAdvance()) || (n == count && proc.currentRampIsZero())); if (proc.currentRampIsZero()) { DstTraits<dstType, premul>::store(proc.currentColor(), dst, n); } else { ramp<dstType, premul>(proc.currentColor(), proc.currentColorGrad(), dst, n, bias4f0, bias4f1); } proc.advance(SkIntToScalar(n)); count -= n; dst += n; if (n & 1) { SkTSwap(bias4f0, bias4f1); } } }
static void intersectWithOrder(const Quadratic& simple1, int order1, const Quadratic& simple2, int order2, Intersections& i) { if (order1 == 3 && order2 == 3) { intersect2(simple1, simple2, i); } else if (order1 <= 2 && order2 <= 2) { intersect((const _Line&) simple1, (const _Line&) simple2, i); } else if (order1 == 3 && order2 <= 2) { intersect(simple1, (const _Line&) simple2, i); } else { SkASSERT(order1 <= 2 && order2 == 3); intersect(simple2, (const _Line&) simple1, i); for (int s = 0; s < i.fUsed; ++s) { SkTSwap(i.fT[0][s], i.fT[1][s]); } } }
void CubicPathToSimple(const SkPath& cubicPath, SkPath* simplePath) { simplePath->reset(); SkDCubic cubic; SkPath::RawIter iter(cubicPath); uint8_t verb; SkPoint pts[4]; while ((verb = iter.next(pts)) != SkPath::kDone_Verb) { switch (verb) { case SkPath::kMove_Verb: simplePath->moveTo(pts[0].fX, pts[0].fY); continue; case SkPath::kLine_Verb: simplePath->lineTo(pts[1].fX, pts[1].fY); break; case SkPath::kQuad_Verb: simplePath->quadTo(pts[1].fX, pts[1].fY, pts[2].fX, pts[2].fY); break; case SkPath::kCubic_Verb: { cubic.set(pts); double tInflects[2]; int inflections = cubic.findInflections(tInflects); if (inflections > 1 && tInflects[0] > tInflects[1]) { SkTSwap(tInflects[0], tInflects[1]); } double lo = 0; for (int index = 0; index <= inflections; ++index) { double hi = index < inflections ? tInflects[index] : 1; SkDCubic part = cubic.subDivide(lo, hi); SkPoint cPts[3]; cPts[0] = part[1].asSkPoint(); cPts[1] = part[2].asSkPoint(); cPts[2] = part[3].asSkPoint(); simplePath->cubicTo(cPts[0].fX, cPts[0].fY, cPts[1].fX, cPts[1].fY, cPts[2].fX, cPts[2].fY); lo = hi; } break; } case SkPath::kClose_Verb: simplePath->close(); break; default: SkDEBUGFAIL("bad verb"); return; } } }
static void srcover_srgb_dst_1(const SkXfermode*, uint32_t dst[], const SkPM4f* src, int count, const SkAlpha aa[]) { Sk4f s4 = src->to4f_pmorder(); Sk4f dst_scale = Sk4f(1 - get_alpha(s4)); if (aa) { for (int i = 0; i < count; ++i) { unsigned a = aa[i]; if (0 == a) { continue; } Sk4f d4 = srgb_4b_to_linear_unit(dst[i]); Sk4f r4; if (a != 0xFF) { const Sk4f s4_aa = scale_by_coverage(s4, a); r4 = s4_aa + d4 * Sk4f(1 - get_alpha(s4_aa)); } else { r4 = s4 + d4 * dst_scale; } dst[i] = to_4b(linear_unit_to_srgb_255f(r4)); } } else { while (count >= 4) { auto d = load_4_srgb(dst); auto s = Sk4x4f{{ src->r() }, { src->g() }, { src->b() }, { src->a() }}; #if defined(SK_PMCOLOR_IS_BGRA) SkTSwap(s.r, s.b); #endif auto invSA = 1.0f - s.a; auto r = s.r + d.r * invSA, g = s.g + d.g * invSA, b = s.b + d.b * invSA, a = s.a + d.a * invSA; store_4_srgb(dst, Sk4x4f{r,g,b,a}); count -= 4; dst += 4; } for (int i = 0; i < count; ++i) { Sk4f d4 = srgb_4b_to_linear_unit(dst[i]); dst[i] = to_4b(linear_unit_to_srgb_255f(s4 + d4 * dst_scale)); } } }
int SkIntersections::horizontal(const SkDLine& line, double y) { double min = line[0].fY; double max = line[1].fY; if (min > max) { SkTSwap(min, max); } if (min > y || max < y) { return fUsed = 0; } if (AlmostEqualUlps(min, max)) { fT[0][0] = 0; fT[0][1] = 1; return fUsed = 2; } fT[0][0] = (y - line[0].fY) / (line[1].fY - line[0].fY); return fUsed = 1; }
int SkIntersections::vertical(const SkDLine& line, double x) { double min = line[0].fX; double max = line[1].fX; if (min > max) { SkTSwap(min, max); } if (!precisely_between(min, x, max)) { return fUsed = 0; } if (AlmostEqualUlps(min, max)) { fT[0][0] = 0; fT[0][1] = 1; return fUsed = 2; } fT[0][0] = (x - line[0].fX) / (line[1].fX - line[0].fX); return fUsed = 1; }
template <DstType D> void srcover_n(const SkXfermode*, uint32_t dst[], const SkPM4f src[], int count, const SkAlpha aa[]) { if (aa) { for (int i = 0; i < count; ++i) { unsigned a = aa[i]; if (0 == a) { continue; } Sk4f s4 = src[i].to4f_pmorder(); Sk4f d4 = load_dst<D>(dst[i]); if (a != 0xFF) { s4 = scale_by_coverage(s4, a); } Sk4f r4 = s4 + d4 * Sk4f(1 - get_alpha(s4)); dst[i] = store_dst<D>(r4); } } else { while (count >= 4 && D == kSRGB_Dst) { auto d = load_4_srgb(dst); auto s = Sk4x4f::Transpose(src->fVec); #if defined(SK_PMCOLOR_IS_BGRA) SkTSwap(s.r, s.b); #endif auto invSA = 1.0f - s.a; auto r = s.r + d.r * invSA, g = s.g + d.g * invSA, b = s.b + d.b * invSA, a = s.a + d.a * invSA; store_4_srgb(dst, Sk4x4f{r,g,b,a}); count -= 4; dst += 4; src += 4; } for (int i = 0; i < count; ++i) { Sk4f s4 = src[i].to4f_pmorder(); Sk4f d4 = load_dst<D>(dst[i]); Sk4f r4 = s4 + d4 * Sk4f(1 - get_alpha(s4)); dst[i] = store_dst<D>(r4); } } }
static int winding_mono_quad(const SkPoint pts[], SkScalar x, SkScalar y) { SkScalar y0 = pts[0].fY; SkScalar y2 = pts[2].fY; int dir = 1; if (y0 > y2) { SkTSwap(y0, y2); dir = -1; } if (y < y0 || y >= y2) { return 0; } // bounds check on X (not required, but maybe faster) #if 0 if (pts[0].fX > x && pts[1].fX > x && pts[2].fX > x) { return 0; } #endif SkScalar roots[2]; int n = SkFindUnitQuadRoots(pts[0].fY - 2 * pts[1].fY + pts[2].fY, 2 * (pts[1].fY - pts[0].fY), pts[0].fY - y, roots); SkASSERT(n <= 1); SkScalar xt; if (0 == n) { SkScalar mid = SkScalarAve(y0, y2); // Need [0] and [2] if dir == 1 // and [2] and [0] if dir == -1 xt = y < mid ? pts[1 - dir].fX : pts[dir - 1].fX; } else { SkScalar t = roots[0]; SkScalar C = pts[0].fX; SkScalar A = pts[2].fX - 2 * pts[1].fX + C; SkScalar B = 2 * (pts[1].fX - C); xt = SkScalarMulAdd(SkScalarMulAdd(A, t, B), t, C); } return xt < x ? dir : 0; }
// Return the number of distinct real roots, and write them into roots[] in // ascending order static int find_quad_roots(float A, float B, float C, float roots[2], bool descendingOrder = false) { SkASSERT(roots); if (A == 0) { return valid_divide(-C, B, roots); } float R = B*B - 4*A*C; if (R < 0) { return 0; } R = sk_float_sqrt(R); #if 1 float Q = B; if (Q < 0) { Q -= R; } else { Q += R; } #else // on 10.6 this was much slower than the above branch :( float Q = B + copysignf(R, B); #endif Q *= -0.5f; if (0 == Q) { roots[0] = 0; return 1; } float r0 = Q / A; float r1 = C / Q; roots[0] = r0 < r1 ? r0 : r1; roots[1] = r0 > r1 ? r0 : r1; if (descendingOrder) { SkTSwap(roots[0], roots[1]); } return 2; }
void SkLinearGradient:: LinearGradient4fContext::shadeSpan(int x, int y, SkPMColor dst[], int count) { SkASSERT(count > 0); float bias0 = 0, bias1 = 0; if (fDither) { static constexpr float dither_cell[] = { -3/8.0f, 1/8.0f, 3/8.0f, -1/8.0f, }; const int rowIndex = (y & 1) << 1; bias0 = dither_cell[rowIndex + 0]; bias1 = dither_cell[rowIndex + 1]; if (x & 1) { SkTSwap(bias0, bias1); } } if (fColorsArePremul) { // In premul interpolation mode, components are pre-scaled by 255 and the store // op is truncating. We pre-bias here to achieve rounding. bias0 += 0.5f; bias1 += 0.5f; this->shadePremulSpan<SkPMColor, ApplyPremul::False>(x, y, dst, count, bias0, bias1); } else { // In unpremul interpolation mode, Components are not pre-scaled. bias0 *= 1/255.0f; bias1 *= 1/255.0f; this->shadePremulSpan<SkPMColor, ApplyPremul::True >(x, y, dst, count, bias0, bias1); } }
bool SkAnimatedImage::Frame::init(const SkImageInfo& info, OnInit onInit) { if (fBitmap.getPixels()) { if (fBitmap.pixelRef()->unique()) { SkAssertResult(fBitmap.setAlphaType(info.alphaType())); return true; } // An SkCanvas provided to onDraw is still holding a reference. // Copy before we decode to ensure that we don't overwrite the // expected contents of the image. if (OnInit::kRestoreIfNecessary == onInit) { SkBitmap tmp; if (!tmp.tryAllocPixels(info)) { return false; } memcpy(tmp.getPixels(), fBitmap.getPixels(), fBitmap.computeByteSize()); SkTSwap(tmp, fBitmap); return true; } } return fBitmap.tryAllocPixels(info); }
void sk_dither_memset16(uint16_t dst[], uint16_t value, uint16_t other, int count) { if (count > 0) { // see if we need to write one short before we can cast to an 4byte ptr // (we do this subtract rather than (unsigned)dst so we don't get warnings // on 64bit machines) if (((char*)dst - (char*)0) & 2) { *dst++ = value; count -= 1; SkTSwap(value, other); } // fast way to set [value,other] pairs #ifdef SK_CPU_BENDIAN sk_memset32((uint32_t*)dst, (value << 16) | other, count >> 1); #else sk_memset32((uint32_t*)dst, (other << 16) | value, count >> 1); #endif if (count & 1) { dst[count - 1] = value; } } }
// Attempt to trim the line to minimally cover the cull rect (currently // only works for horizontal and vertical lines). // Return true if processing should continue; false otherwise. static bool cull_line(SkPoint* pts, const SkStrokeRec& rec, const SkMatrix& ctm, const SkRect* cullRect, const SkScalar intervalLength) { if (nullptr == cullRect) { SkASSERT(false); // Shouldn't ever occur in practice return false; } SkScalar dx = pts[1].x() - pts[0].x(); SkScalar dy = pts[1].y() - pts[0].y(); if ((dx && dy) || (!dx && !dy)) { return false; } SkRect bounds = *cullRect; outset_for_stroke(&bounds, rec); // cullRect is in device space while pts are in the local coordinate system // defined by the ctm. We want our answer in the local coordinate system. SkASSERT(ctm.rectStaysRect()); SkMatrix inv; if (!ctm.invert(&inv)) { return false; } inv.mapRect(&bounds); if (dx) { SkASSERT(dx && !dy); SkScalar minX = pts[0].fX; SkScalar maxX = pts[1].fX; if (dx < 0) { SkTSwap(minX, maxX); } SkASSERT(minX < maxX); if (maxX <= bounds.fLeft || minX >= bounds.fRight) { return false; } // Now we actually perform the chop, removing the excess to the left and // right of the bounds (keeping our new line "in phase" with the dash, // hence the (mod intervalLength). if (minX < bounds.fLeft) { minX = bounds.fLeft - SkScalarMod(bounds.fLeft - minX, intervalLength); } if (maxX > bounds.fRight) { maxX = bounds.fRight + SkScalarMod(maxX - bounds.fRight, intervalLength); } SkASSERT(maxX > minX); if (dx < 0) { SkTSwap(minX, maxX); } pts[0].fX = minX; pts[1].fX = maxX; } else { SkASSERT(dy && !dx); SkScalar minY = pts[0].fY; SkScalar maxY = pts[1].fY; if (dy < 0) { SkTSwap(minY, maxY); } SkASSERT(minY < maxY); if (maxY <= bounds.fTop || minY >= bounds.fBottom) { return false; } // Now we actually perform the chop, removing the excess to the top and // bottom of the bounds (keeping our new line "in phase" with the dash, // hence the (mod intervalLength). if (minY < bounds.fTop) { minY = bounds.fTop - SkScalarMod(bounds.fTop - minY, intervalLength); } if (maxY > bounds.fBottom) { maxY = bounds.fBottom + SkScalarMod(maxY - bounds.fBottom, intervalLength); } SkASSERT(maxY > minY); if (dy < 0) { SkTSwap(minY, maxY); } pts[0].fY = minY; pts[1].fY = maxY; } return true; }
GrTexture* GaussianBlur(GrContext* context, GrTexture* srcTexture, bool canClobberSrc, const SkRect& rect, bool cropToRect, float sigmaX, float sigmaY) { SkASSERT(context); SkIRect clearRect; int scaleFactorX, radiusX; int scaleFactorY, radiusY; int maxTextureSize = context->caps()->maxTextureSize(); sigmaX = adjust_sigma(sigmaX, maxTextureSize, &scaleFactorX, &radiusX); sigmaY = adjust_sigma(sigmaY, maxTextureSize, &scaleFactorY, &radiusY); SkRect srcRect(rect); scale_rect(&srcRect, 1.0f / scaleFactorX, 1.0f / scaleFactorY); srcRect.roundOut(&srcRect); scale_rect(&srcRect, static_cast<float>(scaleFactorX), static_cast<float>(scaleFactorY)); // setup new clip GrClip clip(SkRect::MakeWH(srcRect.width(), srcRect.height())); SkASSERT(kBGRA_8888_GrPixelConfig == srcTexture->config() || kRGBA_8888_GrPixelConfig == srcTexture->config() || kAlpha_8_GrPixelConfig == srcTexture->config()); GrSurfaceDesc desc; desc.fFlags = kRenderTarget_GrSurfaceFlag; desc.fWidth = SkScalarFloorToInt(srcRect.width()); desc.fHeight = SkScalarFloorToInt(srcRect.height()); desc.fConfig = srcTexture->config(); GrTexture* dstTexture; GrTexture* tempTexture; SkAutoTUnref<GrTexture> temp1, temp2; temp1.reset(context->textureProvider()->refScratchTexture( desc, GrTextureProvider::kApprox_ScratchTexMatch)); dstTexture = temp1.get(); if (canClobberSrc) { tempTexture = srcTexture; } else { temp2.reset(context->textureProvider()->refScratchTexture( desc, GrTextureProvider::kApprox_ScratchTexMatch)); tempTexture = temp2.get(); } if (NULL == dstTexture || NULL == tempTexture) { return NULL; } GrDrawContext* drawContext = context->drawContext(); if (!drawContext) { return NULL; } for (int i = 1; i < scaleFactorX || i < scaleFactorY; i *= 2) { GrPaint paint; SkMatrix matrix; matrix.setIDiv(srcTexture->width(), srcTexture->height()); SkRect dstRect(srcRect); if (cropToRect && i == 1) { dstRect.offset(-dstRect.fLeft, -dstRect.fTop); SkRect domain; matrix.mapRect(&domain, rect); domain.inset(i < scaleFactorX ? SK_ScalarHalf / srcTexture->width() : 0.0f, i < scaleFactorY ? SK_ScalarHalf / srcTexture->height() : 0.0f); SkAutoTUnref<GrFragmentProcessor> fp( GrTextureDomainEffect::Create( paint.getProcessorDataManager(), srcTexture, matrix, domain, GrTextureDomain::kDecal_Mode, GrTextureParams::kBilerp_FilterMode)); paint.addColorProcessor(fp); } else { GrTextureParams params(SkShader::kClamp_TileMode, GrTextureParams::kBilerp_FilterMode); paint.addColorTextureProcessor(srcTexture, matrix, params); } scale_rect(&dstRect, i < scaleFactorX ? 0.5f : 1.0f, i < scaleFactorY ? 0.5f : 1.0f); drawContext->drawNonAARectToRect(dstTexture->asRenderTarget(), clip, paint, SkMatrix::I(), dstRect, srcRect); srcRect = dstRect; srcTexture = dstTexture; SkTSwap(dstTexture, tempTexture); } const SkIRect srcIRect = srcRect.roundOut(); // For really small blurs(Certainly no wider than 5x5 on desktop gpus) it is faster to just // launch a single non separable kernel vs two launches if (sigmaX > 0.0f && sigmaY > 0 && (2 * radiusX + 1) * (2 * radiusY + 1) <= MAX_KERNEL_SIZE) { // We shouldn't be scaling because this is a small size blur SkASSERT((scaleFactorX == scaleFactorY) == 1); SkRect dstRect = SkRect::MakeWH(srcRect.width(), srcRect.height()); convolve_gaussian_2d(drawContext, dstTexture->asRenderTarget(), clip, srcRect, dstRect, srcTexture, radiusX, radiusY, sigmaX, sigmaY, cropToRect, srcIRect); srcTexture = dstTexture; srcRect = dstRect; SkTSwap(dstTexture, tempTexture); } else { if (sigmaX > 0.0f) { if (scaleFactorX > 1) { // Clear out a radius to the right of the srcRect to prevent the // X convolution from reading garbage. clearRect = SkIRect::MakeXYWH(srcIRect.fRight, srcIRect.fTop, radiusX, srcIRect.height()); drawContext->clear(srcTexture->asRenderTarget(), &clearRect, 0x0, false); } SkRect dstRect = SkRect::MakeWH(srcRect.width(), srcRect.height()); convolve_gaussian(drawContext, dstTexture->asRenderTarget(), clip, srcRect, dstRect, srcTexture, Gr1DKernelEffect::kX_Direction, radiusX, sigmaX, cropToRect); srcTexture = dstTexture; srcRect = dstRect; SkTSwap(dstTexture, tempTexture); } if (sigmaY > 0.0f) { if (scaleFactorY > 1 || sigmaX > 0.0f) { // Clear out a radius below the srcRect to prevent the Y // convolution from reading garbage. clearRect = SkIRect::MakeXYWH(srcIRect.fLeft, srcIRect.fBottom, srcIRect.width(), radiusY); drawContext->clear(srcTexture->asRenderTarget(), &clearRect, 0x0, false); } SkRect dstRect = SkRect::MakeWH(srcRect.width(), srcRect.height()); convolve_gaussian(drawContext, dstTexture->asRenderTarget(), clip, srcRect, dstRect, srcTexture, Gr1DKernelEffect::kY_Direction, radiusY, sigmaY, cropToRect); srcTexture = dstTexture; srcRect = dstRect; SkTSwap(dstTexture, tempTexture); } } if (scaleFactorX > 1 || scaleFactorY > 1) { // Clear one pixel to the right and below, to accommodate bilinear // upsampling. clearRect = SkIRect::MakeXYWH(srcIRect.fLeft, srcIRect.fBottom, srcIRect.width() + 1, 1); drawContext->clear(srcTexture->asRenderTarget(), &clearRect, 0x0, false); clearRect = SkIRect::MakeXYWH(srcIRect.fRight, srcIRect.fTop, 1, srcIRect.height()); drawContext->clear(srcTexture->asRenderTarget(), &clearRect, 0x0, false); SkMatrix matrix; matrix.setIDiv(srcTexture->width(), srcTexture->height()); GrPaint paint; // FIXME: this should be mitchell, not bilinear. GrTextureParams params(SkShader::kClamp_TileMode, GrTextureParams::kBilerp_FilterMode); paint.addColorTextureProcessor(srcTexture, matrix, params); SkRect dstRect(srcRect); scale_rect(&dstRect, (float) scaleFactorX, (float) scaleFactorY); drawContext->drawNonAARectToRect(dstTexture->asRenderTarget(), clip, paint, SkMatrix::I(), dstRect, srcRect); srcRect = dstRect; srcTexture = dstTexture; SkTSwap(dstTexture, tempTexture); } return SkRef(srcTexture); }
void SkPicture::swap(SkPicture& other) { SkTSwap(fRecord, other.fRecord); SkTSwap(fPlayback, other.fPlayback); SkTSwap(fWidth, other.fWidth); SkTSwap(fHeight, other.fHeight); }
SkOpSegment* FindSortableTop(const SkTArray<SkOpContour*, true>& contourList, SkOpAngle::IncludeType angleIncludeType, bool* firstContour, int* indexPtr, int* endIndexPtr, SkPoint* topLeft, bool* unsortable, bool* done, bool* onlyVertical, bool firstPass) { SkOpSegment* current = findTopSegment(contourList, indexPtr, endIndexPtr, topLeft, unsortable, done, firstPass); if (!current) { return NULL; } const int startIndex = *indexPtr; const int endIndex = *endIndexPtr; if (*firstContour) { current->initWinding(startIndex, endIndex, angleIncludeType); *firstContour = false; return current; } int minIndex = SkMin32(startIndex, endIndex); int sumWinding = current->windSum(minIndex); if (sumWinding == SK_MinS32) { int index = endIndex; int oIndex = startIndex; do { const SkOpSpan& span = current->span(index); if ((oIndex < index ? span.fFromAngle : span.fToAngle) == NULL) { current->addSimpleAngle(index); } sumWinding = current->computeSum(oIndex, index, angleIncludeType); SkTSwap(index, oIndex); } while (sumWinding == SK_MinS32 && index == startIndex); } if (sumWinding != SK_MinS32 && sumWinding != SK_NaN32) { return current; } int contourWinding; int oppContourWinding = 0; // the simple upward projection of the unresolved points hit unsortable angles // shoot rays at right angles to the segment to find its winding, ignoring angle cases bool tryAgain; double tHit; SkScalar hitDx = 0; SkScalar hitOppDx = 0; do { // if current is vertical, find another candidate which is not // if only remaining candidates are vertical, then they can be marked done SkASSERT(*indexPtr != *endIndexPtr && *indexPtr >= 0 && *endIndexPtr >= 0); skipVertical(contourList, ¤t, indexPtr, endIndexPtr); SkASSERT(current); // FIXME: if null, all remaining are vertical SkASSERT(*indexPtr != *endIndexPtr && *indexPtr >= 0 && *endIndexPtr >= 0); tryAgain = false; contourWinding = rightAngleWinding(contourList, ¤t, indexPtr, endIndexPtr, &tHit, &hitDx, &tryAgain, onlyVertical, false); if (*onlyVertical) { return current; } if (tryAgain) { continue; } if (angleIncludeType < SkOpAngle::kBinarySingle) { break; } oppContourWinding = rightAngleWinding(contourList, ¤t, indexPtr, endIndexPtr, &tHit, &hitOppDx, &tryAgain, NULL, true); } while (tryAgain); current->initWinding(*indexPtr, *endIndexPtr, tHit, contourWinding, hitDx, oppContourWinding, hitOppDx); if (current->done()) { return NULL; } return current; }
bool SkScriptRuntime::executeTokens(unsigned char* opCode) { SkOperand2 operand[2]; // 1=accumulator and 2=operand SkScriptEngine2::TypeOp op; size_t ref; int index, size; int registerLoad; SkScriptCallBack* callBack SK_INIT_TO_AVOID_WARNING; do { switch ((op = (SkScriptEngine2::TypeOp) *opCode++)) { case SkScriptEngine2::kArrayToken: // create an array operand[0].fArray = new SkOpArray(SkOperand2::kNoType /*fReturnType*/); break; case SkScriptEngine2::kArrayIndex: // array accessor index = operand[1].fS32; if (index >= operand[0].fArray->count()) { fError = kArrayIndexOutOfBounds; return false; } operand[0] = operand[0].fArray->begin()[index]; break; case SkScriptEngine2::kArrayParam: // array initializer, or function param *operand[0].fArray->append() = operand[1]; break; case SkScriptEngine2::kCallback: memcpy(&index, opCode, sizeof(index)); opCode += sizeof(index); callBack = fCallBackArray[index]; break; case SkScriptEngine2::kFunctionCall: { memcpy(&ref, opCode, sizeof(ref)); opCode += sizeof(ref); SkScriptCallBackFunction* callBackFunction = (SkScriptCallBackFunction*) callBack; if (callBackFunction->invoke(ref, operand[0].fArray, /* params */ &operand[0] /* result */) == false) { fError = kFunctionCallFailed; return false; } } break; case SkScriptEngine2::kMemberOp: { memcpy(&ref, opCode, sizeof(ref)); opCode += sizeof(ref); SkScriptCallBackMember* callBackMember = (SkScriptCallBackMember*) callBack; if (callBackMember->invoke(ref, operand[0].fObject, &operand[0]) == false) { fError = kMemberOpFailed; return false; } } break; case SkScriptEngine2::kPropertyOp: { memcpy(&ref, opCode, sizeof(ref)); opCode += sizeof(ref); SkScriptCallBackProperty* callBackProperty = (SkScriptCallBackProperty*) callBack; if (callBackProperty->getResult(ref, &operand[0])== false) { fError = kPropertyOpFailed; return false; } } break; case SkScriptEngine2::kAccumulatorPop: fRunStack.pop(&operand[0]); break; case SkScriptEngine2::kAccumulatorPush: *fRunStack.push() = operand[0]; break; case SkScriptEngine2::kIntegerAccumulator: case SkScriptEngine2::kIntegerOperand: registerLoad = op - SkScriptEngine2::kIntegerAccumulator; memcpy(&operand[registerLoad].fS32, opCode, sizeof(int32_t)); opCode += sizeof(int32_t); break; case SkScriptEngine2::kScalarAccumulator: case SkScriptEngine2::kScalarOperand: registerLoad = op - SkScriptEngine2::kScalarAccumulator; memcpy(&operand[registerLoad].fScalar, opCode, sizeof(SkScalar)); opCode += sizeof(SkScalar); break; case SkScriptEngine2::kStringAccumulator: case SkScriptEngine2::kStringOperand: { SkString* strPtr = new SkString(); track(strPtr); registerLoad = op - SkScriptEngine2::kStringAccumulator; memcpy(&size, opCode, sizeof(size)); opCode += sizeof(size); strPtr->set((char*) opCode, size); opCode += size; operand[registerLoad].fString = strPtr; } break; case SkScriptEngine2::kStringTrack: // call after kObjectToValue track(operand[0].fString); break; case SkScriptEngine2::kBoxToken: { SkOperand2::OpType type; memcpy(&type, opCode, sizeof(type)); opCode += sizeof(type); SkScriptCallBackConvert* callBackBox = (SkScriptCallBackConvert*) callBack; if (callBackBox->convert(type, &operand[0]) == false) return false; } break; case SkScriptEngine2::kUnboxToken: case SkScriptEngine2::kUnboxToken2: { SkScriptCallBackConvert* callBackUnbox = (SkScriptCallBackConvert*) callBack; if (callBackUnbox->convert(SkOperand2::kObject, &operand[0]) == false) return false; } break; case SkScriptEngine2::kIfOp: case SkScriptEngine2::kLogicalAndInt: memcpy(&size, opCode, sizeof(size)); opCode += sizeof(size); if (operand[0].fS32 == 0) opCode += size; // skip to else (or end of if predicate) break; case SkScriptEngine2::kElseOp: memcpy(&size, opCode, sizeof(size)); opCode += sizeof(size); opCode += size; // if true: after predicate, always skip to end of else break; case SkScriptEngine2::kLogicalOrInt: memcpy(&size, opCode, sizeof(size)); opCode += sizeof(size); if (operand[0].fS32 != 0) opCode += size; // skip to kToBool opcode after || predicate break; // arithmetic conversion ops case SkScriptEngine2::kFlipOpsOp: SkTSwap(operand[0], operand[1]); break; case SkScriptEngine2::kIntToString: case SkScriptEngine2::kIntToString2: case SkScriptEngine2::kScalarToString: case SkScriptEngine2::kScalarToString2: { SkString* strPtr = new SkString(); track(strPtr); if (op == SkScriptEngine2::kIntToString || op == SkScriptEngine2::kIntToString2) strPtr->appendS32(operand[op - SkScriptEngine2::kIntToString].fS32); else strPtr->appendScalar(operand[op - SkScriptEngine2::kScalarToString].fScalar); operand[0].fString = strPtr; } break; case SkScriptEngine2::kIntToScalar: case SkScriptEngine2::kIntToScalar2: operand[0].fScalar = SkScriptEngine2::IntToScalar(operand[op - SkScriptEngine2::kIntToScalar].fS32); break; case SkScriptEngine2::kStringToInt: if (SkParse::FindS32(operand[0].fString->c_str(), &operand[0].fS32) == NULL) return false; break; case SkScriptEngine2::kStringToScalar: case SkScriptEngine2::kStringToScalar2: if (SkParse::FindScalar(operand[0].fString->c_str(), &operand[op - SkScriptEngine2::kStringToScalar].fScalar) == NULL) return false; break; case SkScriptEngine2::kScalarToInt: operand[0].fS32 = SkScalarFloorToInt(operand[0].fScalar); break; // arithmetic ops case SkScriptEngine2::kAddInt: operand[0].fS32 += operand[1].fS32; break; case SkScriptEngine2::kAddScalar: operand[0].fScalar += operand[1].fScalar; break; case SkScriptEngine2::kAddString: // if (fTrackString.find(operand[1].fString) < 0) { // operand[1].fString = SkNEW_ARGS(SkString, (*operand[1].fString)); // track(operand[1].fString); // } operand[0].fString->append(*operand[1].fString); break; case SkScriptEngine2::kBitAndInt: operand[0].fS32 &= operand[1].fS32; break; case SkScriptEngine2::kBitNotInt: operand[0].fS32 = ~operand[0].fS32; break; case SkScriptEngine2::kBitOrInt: operand[0].fS32 |= operand[1].fS32; break; case SkScriptEngine2::kDivideInt: SkASSERT(operand[1].fS32 != 0); if (operand[1].fS32 == 0) operand[0].fS32 = operand[0].fS32 == 0 ? SK_NaN32 : operand[0].fS32 > 0 ? SK_MaxS32 : -SK_MaxS32; else if (operand[1].fS32 != 0) // throw error on divide by zero? operand[0].fS32 /= operand[1].fS32; break; case SkScriptEngine2::kDivideScalar: if (operand[1].fScalar == 0) operand[0].fScalar = operand[0].fScalar == 0 ? SK_ScalarNaN : operand[0].fScalar > 0 ? SK_ScalarMax : -SK_ScalarMax; else operand[0].fScalar = SkScalarDiv(operand[0].fScalar, operand[1].fScalar); break; case SkScriptEngine2::kEqualInt: operand[0].fS32 = operand[0].fS32 == operand[1].fS32; break; case SkScriptEngine2::kEqualScalar: operand[0].fS32 = operand[0].fScalar == operand[1].fScalar; break; case SkScriptEngine2::kEqualString: operand[0].fS32 = *operand[0].fString == *operand[1].fString; break; case SkScriptEngine2::kGreaterEqualInt: operand[0].fS32 = operand[0].fS32 >= operand[1].fS32; break; case SkScriptEngine2::kGreaterEqualScalar: operand[0].fS32 = operand[0].fScalar >= operand[1].fScalar; break; case SkScriptEngine2::kGreaterEqualString: operand[0].fS32 = strcmp(operand[0].fString->c_str(), operand[1].fString->c_str()) >= 0; break; case SkScriptEngine2::kToBool: operand[0].fS32 = !! operand[0].fS32; break; case SkScriptEngine2::kLogicalNotInt: operand[0].fS32 = ! operand[0].fS32; break; case SkScriptEngine2::kMinusInt: operand[0].fS32 = -operand[0].fS32; break; case SkScriptEngine2::kMinusScalar: operand[0].fScalar = -operand[0].fScalar; break; case SkScriptEngine2::kModuloInt: operand[0].fS32 %= operand[1].fS32; break; case SkScriptEngine2::kModuloScalar: operand[0].fScalar = SkScalarMod(operand[0].fScalar, operand[1].fScalar); break; case SkScriptEngine2::kMultiplyInt: operand[0].fS32 *= operand[1].fS32; break; case SkScriptEngine2::kMultiplyScalar: operand[0].fScalar = SkScalarMul(operand[0].fScalar, operand[1].fScalar); break; case SkScriptEngine2::kShiftLeftInt: operand[0].fS32 <<= operand[1].fS32; break; case SkScriptEngine2::kShiftRightInt: operand[0].fS32 >>= operand[1].fS32; break; case SkScriptEngine2::kSubtractInt: operand[0].fS32 -= operand[1].fS32; break; case SkScriptEngine2::kSubtractScalar: operand[0].fScalar -= operand[1].fScalar; break; case SkScriptEngine2::kXorInt: operand[0].fS32 ^= operand[1].fS32; break; case SkScriptEngine2::kEnd: goto done; case SkScriptEngine2::kNop: SkASSERT(0); default: break; } } while (true); done: fRunStack.push(operand[0]); return true; }