static void pack4xHToLCD16(const SkBitmap& src, const SkMask& dst, const SkMaskGamma::PreBlend& maskPreBlend) { #define SAMPLES_PER_PIXEL 4 #define LCD_PER_PIXEL 3 SkASSERT(kAlpha_8_SkColorType == src.colorType()); SkASSERT(SkMask::kLCD16_Format == dst.fFormat); const int sample_width = src.width(); const int height = src.height(); uint16_t* dstP = (uint16_t*)dst.fImage; size_t dstRB = dst.fRowBytes; // An N tap FIR is defined by // out[n] = coeff[0]*x[n] + coeff[1]*x[n-1] + ... + coeff[N]*x[n-N] // or // out[n] = sum(i, 0, N, coeff[i]*x[n-i]) // The strategy is to use one FIR (different coefficients) for each of r, g, and b. // This means using every 4th FIR output value of each FIR and discarding the rest. // The FIRs are aligned, and the coefficients reach 5 samples to each side of their 'center'. // (For r and b this is technically incorrect, but the coeffs outside round to zero anyway.) // These are in some fixed point repesentation. // Adding up to more than one simulates ink spread. // For implementation reasons, these should never add up to more than two. // Coefficients determined by a gausian where 5 samples = 3 std deviations (0x110 'contrast'). // Calculated using tools/generate_fir_coeff.py // With this one almost no fringing is ever seen, but it is imperceptibly blurry. // The lcd smoothed text is almost imperceptibly different from gray, // but is still sharper on small stems and small rounded corners than gray. // This also seems to be about as wide as one can get and only have a three pixel kernel. // TODO: caculate these at runtime so parameters can be adjusted (esp contrast). static const unsigned int coefficients[LCD_PER_PIXEL][SAMPLES_PER_PIXEL*3] = { //The red subpixel is centered inside the first sample (at 1/6 pixel), and is shifted. { 0x03, 0x0b, 0x1c, 0x33, 0x40, 0x39, 0x24, 0x10, 0x05, 0x01, 0x00, 0x00, }, //The green subpixel is centered between two samples (at 1/2 pixel), so is symetric { 0x00, 0x02, 0x08, 0x16, 0x2b, 0x3d, 0x3d, 0x2b, 0x16, 0x08, 0x02, 0x00, }, //The blue subpixel is centered inside the last sample (at 5/6 pixel), and is shifted. { 0x00, 0x00, 0x01, 0x05, 0x10, 0x24, 0x39, 0x40, 0x33, 0x1c, 0x0b, 0x03, }, }; for (int y = 0; y < height; ++y) { const uint8_t* srcP = src.getAddr8(0, y); // TODO: this fir filter implementation is straight forward, but slow. // It should be possible to make it much faster. for (int sample_x = -4, pixel_x = 0; sample_x < sample_width + 4; sample_x += 4, ++pixel_x) { int fir[LCD_PER_PIXEL] = { 0 }; for (int sample_index = SkMax32(0, sample_x - 4), coeff_index = sample_index - (sample_x - 4) ; sample_index < SkMin32(sample_x + 8, sample_width) ; ++sample_index, ++coeff_index) { int sample_value = srcP[sample_index]; for (int subpxl_index = 0; subpxl_index < LCD_PER_PIXEL; ++subpxl_index) { fir[subpxl_index] += coefficients[subpxl_index][coeff_index] * sample_value; } } for (int subpxl_index = 0; subpxl_index < LCD_PER_PIXEL; ++subpxl_index) { fir[subpxl_index] /= 0x100; fir[subpxl_index] = SkMin32(fir[subpxl_index], 255); } U8CPU r = sk_apply_lut_if<APPLY_PREBLEND>(fir[0], maskPreBlend.fR); U8CPU g = sk_apply_lut_if<APPLY_PREBLEND>(fir[1], maskPreBlend.fG); U8CPU b = sk_apply_lut_if<APPLY_PREBLEND>(fir[2], maskPreBlend.fB); #if SK_SHOW_TEXT_BLIT_COVERAGE r = SkMax32(r, 10); g = SkMax32(g, 10); b = SkMax32(b, 10); #endif dstP[pixel_x] = SkPack888ToRGB16(r, g, b); } dstP = (uint16_t*)((char*)dstP + dstRB); } }
static inline uint32_t get_overlap(const SkIRect& rect1, const SkIRect& rect2) { // I suspect there's a more efficient way of computing this... return SkMax32(0, SkMin32(rect1.fRight, rect2.fRight) - SkMax32(rect1.fLeft, rect2.fLeft)) * SkMax32(0, SkMin32(rect1.fBottom, rect2.fBottom) - SkMax32(rect1.fTop, rect2.fTop)); }
SkOpSegment* FindChase(SkTDArray<SkOpSpan*>& chase, int& tIndex, int& endIndex) { while (chase.count()) { SkOpSpan* span; chase.pop(&span); const SkOpSpan& backPtr = span->fOther->span(span->fOtherIndex); SkOpSegment* segment = backPtr.fOther; tIndex = backPtr.fOtherIndex; SkSTArray<SkOpAngle::kStackBasedCount, SkOpAngle, true> angles; int done = 0; if (segment->activeAngle(tIndex, &done, &angles)) { SkOpAngle* last = angles.end() - 1; tIndex = last->start(); endIndex = last->end(); #if TRY_ROTATE *chase.insert(0) = span; #else *chase.append() = span; #endif return last->segment(); } if (done == angles.count()) { continue; } SkSTArray<SkOpAngle::kStackBasedCount, SkOpAngle*, true> sorted; bool sortable = SkOpSegment::SortAngles(angles, &sorted, SkOpSegment::kMayBeUnordered_SortAngleKind); int angleCount = sorted.count(); #if DEBUG_SORT sorted[0]->segment()->debugShowSort(__FUNCTION__, sorted, 0, 0, 0); #endif if (!sortable) { continue; } // find first angle, initialize winding to computed fWindSum int firstIndex = -1; const SkOpAngle* angle; int winding; do { angle = sorted[++firstIndex]; segment = angle->segment(); winding = segment->windSum(angle); } while (winding == SK_MinS32); int spanWinding = segment->spanSign(angle->start(), angle->end()); #if DEBUG_WINDING SkDebugf("%s winding=%d spanWinding=%d\n", __FUNCTION__, winding, spanWinding); #endif // turn span winding into contour winding if (spanWinding * winding < 0) { winding += spanWinding; } #if DEBUG_SORT segment->debugShowSort(__FUNCTION__, sorted, firstIndex, winding, 0); #endif // we care about first sign and whether wind sum indicates this // edge is inside or outside. Maybe need to pass span winding // or first winding or something into this function? // advance to first undone angle, then return it and winding // (to set whether edges are active or not) int nextIndex = firstIndex + 1; int lastIndex = firstIndex != 0 ? firstIndex : angleCount; angle = sorted[firstIndex]; winding -= angle->segment()->spanSign(angle); do { SkASSERT(nextIndex != firstIndex); if (nextIndex == angleCount) { nextIndex = 0; } angle = sorted[nextIndex]; segment = angle->segment(); int maxWinding = winding; winding -= segment->spanSign(angle); #if DEBUG_SORT SkDebugf("%s id=%d maxWinding=%d winding=%d sign=%d\n", __FUNCTION__, segment->debugID(), maxWinding, winding, angle->sign()); #endif tIndex = angle->start(); endIndex = angle->end(); int lesser = SkMin32(tIndex, endIndex); const SkOpSpan& nextSpan = segment->span(lesser); if (!nextSpan.fDone) { // FIXME: this be wrong? assign startWinding if edge is in // same direction. If the direction is opposite, winding to // assign is flipped sign or +/- 1? if (SkOpSegment::UseInnerWinding(maxWinding, winding)) { maxWinding = winding; } segment->markAndChaseWinding(angle, maxWinding, 0); break; } } while (++nextIndex != lastIndex); *chase.insert(0) = span; return segment; } return NULL; }
static int boxBlurInterp(const uint8_t* src, int src_y_stride, uint8_t* dst, int radius, int width, int height, bool transpose, uint8_t outer_weight) { int diameter = radius * 2; int kernelSize = diameter + 1; int border = SkMin32(width, diameter); int inner_weight = 255 - outer_weight; outer_weight += outer_weight >> 7; inner_weight += inner_weight >> 7; uint32_t outer_scale = (outer_weight << 16) / kernelSize; uint32_t inner_scale = (inner_weight << 16) / (kernelSize - 2); uint32_t half = 1 << 23; int new_width = width + diameter; int dst_x_stride = transpose ? height : 1; int dst_y_stride = transpose ? 1 : new_width; for (int y = 0; y < height; ++y) { uint32_t outer_sum = 0, inner_sum = 0; uint8_t* dptr = dst + y * dst_y_stride; const uint8_t* right = src + y * src_y_stride; const uint8_t* left = right; int x = 0; #define LEFT_BORDER_ITER \ inner_sum = outer_sum; \ outer_sum += *right++; \ *dptr = (outer_sum * outer_scale + inner_sum * inner_scale + half) >> 24; \ dptr += dst_x_stride; #ifdef UNROLL_SEPARABLE_LOOPS for (;x < border - 16; x += 16) { LEFT_BORDER_ITER LEFT_BORDER_ITER LEFT_BORDER_ITER LEFT_BORDER_ITER LEFT_BORDER_ITER LEFT_BORDER_ITER LEFT_BORDER_ITER LEFT_BORDER_ITER LEFT_BORDER_ITER LEFT_BORDER_ITER LEFT_BORDER_ITER LEFT_BORDER_ITER LEFT_BORDER_ITER LEFT_BORDER_ITER LEFT_BORDER_ITER LEFT_BORDER_ITER } #endif for (;x < border; ++x) { LEFT_BORDER_ITER } #undef LEFT_BORDER_ITER for (int x = width; x < diameter; ++x) { *dptr = (outer_sum * outer_scale + inner_sum * inner_scale + half) >> 24; dptr += dst_x_stride; } x = diameter; #define CENTER_ITER \ inner_sum = outer_sum - *left; \ outer_sum += *right++; \ *dptr = (outer_sum * outer_scale + inner_sum * inner_scale + half) >> 24; \ dptr += dst_x_stride; \ outer_sum -= *left++; #ifdef UNROLL_SEPARABLE_LOOPS for (; x < width - 16; x += 16) { CENTER_ITER CENTER_ITER CENTER_ITER CENTER_ITER CENTER_ITER CENTER_ITER CENTER_ITER CENTER_ITER CENTER_ITER CENTER_ITER CENTER_ITER CENTER_ITER CENTER_ITER CENTER_ITER CENTER_ITER CENTER_ITER } #endif for (; x < width; ++x) { CENTER_ITER } #undef CENTER_ITER #define RIGHT_BORDER_ITER \ inner_sum = outer_sum - *left++; \ *dptr = (outer_sum * outer_scale + inner_sum * inner_scale + half) >> 24; \ dptr += dst_x_stride; \ outer_sum = inner_sum; x = 0; #ifdef UNROLL_SEPARABLE_LOOPS for (; x < border - 16; x += 16) { RIGHT_BORDER_ITER RIGHT_BORDER_ITER RIGHT_BORDER_ITER RIGHT_BORDER_ITER RIGHT_BORDER_ITER RIGHT_BORDER_ITER RIGHT_BORDER_ITER RIGHT_BORDER_ITER RIGHT_BORDER_ITER RIGHT_BORDER_ITER RIGHT_BORDER_ITER RIGHT_BORDER_ITER RIGHT_BORDER_ITER RIGHT_BORDER_ITER RIGHT_BORDER_ITER RIGHT_BORDER_ITER } #endif for (; x < border; ++x) { RIGHT_BORDER_ITER } #undef RIGHT_BORDER_ITER SkASSERT(outer_sum == 0 && inner_sum == 0); } return new_width; }
SkRTree::Branch SkRTree::bulkLoad(SkTDArray<Branch>* branches, int level) { if (branches->count() == 1) { // Only one branch: it will be the root Branch out = (*branches)[0]; branches->rewind(); return out; } else { // First we sort the whole list by y coordinates SkQSort<int, Branch>(level, branches->begin(), branches->end() - 1, &RectLessY); int numBranches = branches->count() / fMaxChildren; int remainder = branches->count() % fMaxChildren; int newBranches = 0; if (0 != remainder) { ++numBranches; // If the remainder isn't enough to fill a node, we'll need to add fewer nodes to // some other branches to make up for it if (remainder >= fMinChildren) { remainder = 0; } else { remainder = fMinChildren - remainder; } } int numStrips = SkScalarCeil(SkScalarSqrt(SkIntToScalar(numBranches) * SkScalarInvert(fAspectRatio))); int numTiles = SkScalarCeil(SkIntToScalar(numBranches) / SkIntToScalar(numStrips)); int currentBranch = 0; for (int i = 0; i < numStrips; ++i) { int begin = currentBranch; int end = currentBranch + numTiles * fMaxChildren - SkMin32(remainder, (fMaxChildren - fMinChildren) * numTiles); if (end > branches->count()) { end = branches->count(); } // Now we sort horizontal strips of rectangles by their x coords SkQSort<int, Branch>(level, branches->begin() + begin, branches->begin() + end - 1, &RectLessX); for (int j = 0; j < numTiles && currentBranch < branches->count(); ++j) { int incrementBy = fMaxChildren; if (remainder != 0) { // if need be, omit some nodes to make up for remainder if (remainder <= fMaxChildren - fMinChildren) { incrementBy -= remainder; remainder = 0; } else { incrementBy = fMinChildren; remainder -= fMaxChildren - fMinChildren; } } Node* n = allocateNode(level); n->fNumChildren = 1; *n->child(0) = (*branches)[currentBranch]; Branch b; b.fBounds = (*branches)[currentBranch].fBounds; b.fChild.subtree = n; ++currentBranch; for (int k = 1; k < incrementBy && currentBranch < branches->count(); ++k) { b.fBounds.join((*branches)[currentBranch].fBounds); *n->child(k) = (*branches)[currentBranch]; ++n->fNumChildren; ++currentBranch; } (*branches)[newBranches] = b; ++newBranches; } } branches->setCount(newBranches); return this->bulkLoad(branches, level + 1); } }
void PlatformGraphicsContextSkia::drawLine(const IntPoint& point1, const IntPoint& point2) { StrokeStyle style = m_state->strokeStyle; if (style == NoStroke) return; SkPaint paint; SkCanvas* canvas = mCanvas; const int idx = SkAbs32(point2.x() - point1.x()); const int idy = SkAbs32(point2.y() - point1.y()); // Special-case horizontal and vertical lines that are really just dots if (setupPaintStroke(&paint, 0, !idy) && (!idx || !idy)) { const SkScalar diameter = paint.getStrokeWidth(); const SkScalar radius = SkScalarHalf(diameter); SkScalar x = SkIntToScalar(SkMin32(point1.x(), point2.x())); SkScalar y = SkIntToScalar(SkMin32(point1.y(), point2.y())); SkScalar dx, dy; int count; SkRect bounds; if (!idy) { // Horizontal bounds.set(x, y - radius, x + SkIntToScalar(idx), y + radius); x += radius; dx = diameter * 2; dy = 0; count = idx; } else { // Vertical bounds.set(x - radius, y, x + radius, y + SkIntToScalar(idy)); y += radius; dx = 0; dy = diameter * 2; count = idy; } // The actual count is the number of ONs we hit alternating // ON(diameter), OFF(diameter), ... { SkScalar width = SkScalarDiv(SkIntToScalar(count), diameter); // Now compute the number of cells (ON and OFF) count = SkScalarRound(width); // Now compute the number of ONs count = (count + 1) >> 1; } SkAutoMalloc storage(count * sizeof(SkPoint)); SkPoint* verts = (SkPoint*)storage.get(); // Now build the array of vertices to past to drawPoints for (int i = 0; i < count; i++) { verts[i].set(x, y); x += dx; y += dy; } paint.setStyle(SkPaint::kFill_Style); paint.setPathEffect(0); // Clipping to bounds is not required for correctness, but it does // allow us to reject the entire array of points if we are completely // offscreen. This is common in a webpage for android, where most of // the content is clipped out. If drawPoints took an (optional) bounds // parameter, that might even be better, as we would *just* use it for // culling, and not both wacking the canvas' save/restore stack. canvas->save(SkCanvas::kClip_SaveFlag); canvas->clipRect(bounds); canvas->drawPoints(SkCanvas::kPoints_PointMode, count, verts, paint); canvas->restore(); } else {
static uint64_t compress_latc_block(uint8_t block[16]) { // Just do a simple min/max but choose which of the // two palettes is better uint8_t maxVal = 0; uint8_t minVal = 255; for (int i = 0; i < 16; ++i) { maxVal = SkMax32(maxVal, block[i]); minVal = SkMin32(minVal, block[i]); } // Generate palettes uint8_t palettes[2][8]; // Straight linear ramp palettes[0][0] = maxVal; palettes[0][1] = minVal; for (int i = 1; i < 7; ++i) { palettes[0][i+1] = ((7-i)*maxVal + i*minVal) / 7; } // Smaller linear ramp with min and max byte values at the end. palettes[1][0] = minVal; palettes[1][1] = maxVal; for (int i = 1; i < 5; ++i) { palettes[1][i+1] = ((5-i)*maxVal + i*minVal) / 5; } palettes[1][6] = 0; palettes[1][7] = 255; // Figure out which of the two is better: // - accumError holds the accumulated error for each pixel from // the associated palette // - indices holds the best indices for each palette in the // bottom 48 (16*3) bits. uint32_t accumError[2] = { 0, 0 }; uint64_t indices[2] = { 0, 0 }; for (int i = 15; i >= 0; --i) { // For each palette: // 1. Retreive the result of this pixel // 2. Store the error in accumError // 3. Store the minimum palette index in indices. for (int p = 0; p < 2; ++p) { uint32_t result = compute_error(block[i], palettes[p]); accumError[p] += (result >> 8); indices[p] <<= 3; indices[p] |= result & 7; } } SkASSERT(indices[0] < (static_cast<uint64_t>(1) << 48)); SkASSERT(indices[1] < (static_cast<uint64_t>(1) << 48)); uint8_t paletteIdx = (accumError[0] > accumError[1]) ? 0 : 1; // Assemble the compressed block. uint64_t result = 0; // Jam the first two palette entries into the bottom 16 bits of // a 64 bit integer. Based on the palette that we chose, one will // be larger than the other and it will select the proper palette. result |= static_cast<uint64_t>(palettes[paletteIdx][0]); result |= static_cast<uint64_t>(palettes[paletteIdx][1]) << 8; // Jam the indices into the top 48 bits. result |= indices[paletteIdx] << 16; // We assume everything is little endian, if it's not then make it so. return SkEndian_SwapLE64(result); }
// Returns true if this method handled the glyph, false if needs to be passed to fallback // bool GrDistanceFieldTextContext::appendGlyph(GrGlyph::PackedID packed, SkScalar sx, SkScalar sy, GrFontScaler* scaler) { if (NULL == fDrawTarget) { return true; } if (NULL == fStrike) { fStrike = fContext->getFontCache()->getStrike(scaler, true); } GrGlyph* glyph = fStrike->getGlyph(packed, scaler); if (NULL == glyph || glyph->fBounds.isEmpty()) { return true; } // fallback to color glyph support if (kA8_GrMaskFormat != glyph->fMaskFormat) { return false; } SkScalar dx = SkIntToScalar(glyph->fBounds.fLeft + SK_DistanceFieldInset); SkScalar dy = SkIntToScalar(glyph->fBounds.fTop + SK_DistanceFieldInset); SkScalar width = SkIntToScalar(glyph->fBounds.width() - 2*SK_DistanceFieldInset); SkScalar height = SkIntToScalar(glyph->fBounds.height() - 2*SK_DistanceFieldInset); SkScalar scale = fTextRatio; dx *= scale; dy *= scale; sx += dx; sy += dy; width *= scale; height *= scale; SkRect glyphRect = SkRect::MakeXYWH(sx, sy, width, height); // check if we clipped out SkRect dstRect; const SkMatrix& ctm = fContext->getMatrix(); (void) ctm.mapRect(&dstRect, glyphRect); if (fClipRect.quickReject(SkScalarTruncToInt(dstRect.left()), SkScalarTruncToInt(dstRect.top()), SkScalarTruncToInt(dstRect.right()), SkScalarTruncToInt(dstRect.bottom()))) { // SkCLZ(3); // so we can set a break-point in the debugger return true; } if (NULL == glyph->fPlot) { if (!fStrike->glyphTooLargeForAtlas(glyph)) { if (fStrike->addGlyphToAtlas(glyph, scaler)) { goto HAS_ATLAS; } // try to clear out an unused plot before we flush if (fContext->getFontCache()->freeUnusedPlot(fStrike, glyph) && fStrike->addGlyphToAtlas(glyph, scaler)) { goto HAS_ATLAS; } if (c_DumpFontCache) { #ifdef SK_DEVELOPER fContext->getFontCache()->dump(); #endif } // before we purge the cache, we must flush any accumulated draws this->flush(); fContext->flush(); // we should have an unused plot now if (fContext->getFontCache()->freeUnusedPlot(fStrike, glyph) && fStrike->addGlyphToAtlas(glyph, scaler)) { goto HAS_ATLAS; } } if (NULL == glyph->fPath) { SkPath* path = SkNEW(SkPath); if (!scaler->getGlyphPath(glyph->glyphID(), path)) { // flag the glyph as being dead? delete path; return true; } glyph->fPath = path; } // flush any accumulated draws before drawing this glyph as a path. this->flush(); GrContext::AutoMatrix am; SkMatrix ctm; ctm.setScale(fTextRatio, fTextRatio); ctm.postTranslate(sx - dx, sy - dy); GrPaint tmpPaint(fPaint); am.setPreConcat(fContext, ctm, &tmpPaint); GrStrokeInfo strokeInfo(SkStrokeRec::kFill_InitStyle); fContext->drawPath(tmpPaint, *glyph->fPath, strokeInfo); // remove this glyph from the vertices we need to allocate fTotalVertexCount -= kVerticesPerGlyph; return true; } HAS_ATLAS: SkASSERT(glyph->fPlot); GrDrawTarget::DrawToken drawToken = fDrawTarget->getCurrentDrawToken(); glyph->fPlot->setDrawToken(drawToken); GrTexture* texture = glyph->fPlot->texture(); SkASSERT(texture); if (fCurrTexture != texture || fCurrVertex + kVerticesPerGlyph > fTotalVertexCount) { this->flush(); fCurrTexture = texture; fCurrTexture->ref(); } bool useColorVerts = !fUseLCDText; if (NULL == fVertices) { int maxQuadVertices = kVerticesPerGlyph * fContext->getQuadIndexBuffer()->maxQuads(); fAllocVertexCount = SkMin32(fTotalVertexCount, maxQuadVertices); fVertices = alloc_vertices(fDrawTarget, fAllocVertexCount, useColorVerts); } SkFixed tx = SkIntToFixed(glyph->fAtlasLocation.fX + SK_DistanceFieldInset); SkFixed ty = SkIntToFixed(glyph->fAtlasLocation.fY + SK_DistanceFieldInset); SkFixed tw = SkIntToFixed(glyph->fBounds.width() - 2*SK_DistanceFieldInset); SkFixed th = SkIntToFixed(glyph->fBounds.height() - 2*SK_DistanceFieldInset); fVertexBounds.joinNonEmptyArg(glyphRect); size_t vertSize = get_vertex_stride(useColorVerts); SkPoint* positions = reinterpret_cast<SkPoint*>( reinterpret_cast<intptr_t>(fVertices) + vertSize * fCurrVertex); positions->setRectFan(glyphRect.fLeft, glyphRect.fTop, glyphRect.fRight, glyphRect.fBottom, vertSize); // The texture coords are last in both the with and without color vertex layouts. SkPoint* textureCoords = reinterpret_cast<SkPoint*>( reinterpret_cast<intptr_t>(positions) + vertSize - sizeof(SkPoint)); textureCoords->setRectFan(SkFixedToFloat(texture->texturePriv().normalizeFixedX(tx)), SkFixedToFloat(texture->texturePriv().normalizeFixedY(ty)), SkFixedToFloat(texture->texturePriv().normalizeFixedX(tx + tw)), SkFixedToFloat(texture->texturePriv().normalizeFixedY(ty + th)), vertSize); if (useColorVerts) { // color comes after position. GrColor* colors = reinterpret_cast<GrColor*>(positions + 1); for (int i = 0; i < 4; ++i) { *colors = fPaint.getColor(); colors = reinterpret_cast<GrColor*>(reinterpret_cast<intptr_t>(colors) + vertSize); } } fCurrVertex += 4; return true; }
void SkOpAngle::debugValidateLoop() const { const SkOpAngle* first = this; const SkOpAngle* next = first; SK_ALWAYSBREAK(first->next() != first); int signSum = 0; int oppSum = 0; bool firstOperand = fSegment->operand(); bool unorderable = false; do { unorderable |= next->fUnorderable; const SkOpSegment* segment = next->fSegment; bool operandsMatch = firstOperand == segment->operand(); signSum += operandsMatch ? segment->spanSign(next) : segment->oppSign(next); oppSum += operandsMatch ? segment->oppSign(next) : segment->spanSign(next); const SkOpSpan& span = segment->span(SkMin32(next->fStart, next->fEnd)); if (segment->_xor()) { // SK_ALWAYSBREAK(span.fWindValue == 1); // SK_ALWAYSBREAK(span.fWindSum == SK_MinS32 || span.fWindSum == 1); } if (segment->oppXor()) { SK_ALWAYSBREAK(span.fOppValue == 0 || abs(span.fOppValue) == 1); // SK_ALWAYSBREAK(span.fOppSum == SK_MinS32 || span.fOppSum == 0 || abs(span.fOppSum) == 1); } next = next->next(); if (!next) { return; } } while (next != first); if (unorderable) { return; } SK_ALWAYSBREAK(!signSum || fSegment->_xor()); SK_ALWAYSBREAK(!oppSum || fSegment->oppXor()); int lastWinding; int lastOppWinding; int winding; int oppWinding; do { const SkOpSegment* segment = next->fSegment; const SkOpSpan& span = segment->span(SkMin32(next->fStart, next->fEnd)); winding = span.fWindSum; if (winding != SK_MinS32) { // SK_ALWAYSBREAK(winding != 0); SK_ALWAYSBREAK(SkPathOpsDebug::ValidWind(winding)); lastWinding = winding; int diffWinding = segment->spanSign(next); if (!segment->_xor()) { SK_ALWAYSBREAK(diffWinding != 0); bool sameSign = (winding > 0) == (diffWinding > 0); winding -= sameSign ? diffWinding : -diffWinding; SK_ALWAYSBREAK(SkPathOpsDebug::ValidWind(winding)); SK_ALWAYSBREAK(abs(winding) <= abs(lastWinding)); if (!sameSign) { SkTSwap(winding, lastWinding); } } lastOppWinding = oppWinding = span.fOppSum; if (oppWinding != SK_MinS32 && !segment->oppXor()) { int oppDiffWinding = segment->oppSign(next); // SK_ALWAYSBREAK(abs(oppDiffWinding) <= abs(diffWinding) || segment->_xor()); if (oppDiffWinding) { bool oppSameSign = (oppWinding > 0) == (oppDiffWinding > 0); oppWinding -= oppSameSign ? oppDiffWinding : -oppDiffWinding; SK_ALWAYSBREAK(SkPathOpsDebug::ValidWind(oppWinding)); SK_ALWAYSBREAK(abs(oppWinding) <= abs(lastOppWinding)); if (!oppSameSign) { SkTSwap(oppWinding, lastOppWinding); } } } firstOperand = segment->operand(); break; } SK_ALWAYSBREAK(span.fOppSum == SK_MinS32); next = next->next(); } while (next != first); if (winding == SK_MinS32) { return; } SK_ALWAYSBREAK(oppWinding == SK_MinS32 || SkPathOpsDebug::ValidWind(oppWinding)); first = next; next = next->next(); do { const SkOpSegment* segment = next->fSegment; lastWinding = winding; lastOppWinding = oppWinding; bool operandsMatch = firstOperand == segment->operand(); if (operandsMatch) { if (!segment->_xor()) { winding -= segment->spanSign(next); SK_ALWAYSBREAK(winding != lastWinding); SK_ALWAYSBREAK(SkPathOpsDebug::ValidWind(winding)); } if (!segment->oppXor()) { int oppDiffWinding = segment->oppSign(next); if (oppWinding != SK_MinS32) { oppWinding -= oppDiffWinding; SK_ALWAYSBREAK(SkPathOpsDebug::ValidWind(oppWinding)); } else { SK_ALWAYSBREAK(oppDiffWinding == 0); } } } else { if (!segment->oppXor()) { winding -= segment->oppSign(next); SK_ALWAYSBREAK(SkPathOpsDebug::ValidWind(winding)); } if (!segment->_xor()) { oppWinding -= segment->spanSign(next); SK_ALWAYSBREAK(oppWinding != lastOppWinding); SK_ALWAYSBREAK(SkPathOpsDebug::ValidWind(oppWinding)); } } bool useInner = SkOpSegment::UseInnerWinding(lastWinding, winding); int sumWinding = useInner ? winding : lastWinding; bool oppUseInner = SkOpSegment::UseInnerWinding(lastOppWinding, oppWinding); int oppSumWinding = oppUseInner ? oppWinding : lastOppWinding; if (!operandsMatch) { SkTSwap(useInner, oppUseInner); SkTSwap(sumWinding, oppSumWinding); } const SkOpSpan& span = segment->span(SkMin32(next->fStart, next->fEnd)); if (winding == -lastWinding) { if (span.fWindSum != SK_MinS32) { SkDebugf("%s useInner=%d spanSign=%d lastWinding=%d winding=%d windSum=%d\n", __FUNCTION__, useInner, segment->spanSign(next), lastWinding, winding, span.fWindSum); } } if (oppWinding != SK_MinS32) { if (span.fOppSum != SK_MinS32) { SK_ALWAYSBREAK(span.fOppSum == oppSumWinding || segment->oppXor() || segment->_xor()); } } else { SK_ALWAYSBREAK(!firstOperand); SK_ALWAYSBREAK(!segment->operand()); SK_ALWAYSBREAK(!span.fOppValue); } next = next->next(); } while (next != first); }
SkOpSegment* FindChase(SkTDArray<SkOpSpan*>* chase, int* tIndex, int* endIndex) { while (chase->count()) { SkOpSpan* span; chase->pop(&span); const SkOpSpan& backPtr = span->fOther->span(span->fOtherIndex); SkOpSegment* segment = backPtr.fOther; *tIndex = backPtr.fOtherIndex; bool sortable = true; bool done = true; *endIndex = -1; if (const SkOpAngle* last = segment->activeAngle(*tIndex, tIndex, endIndex, &done, &sortable)) { *tIndex = last->start(); *endIndex = last->end(); #if TRY_ROTATE *chase->insert(0) = span; #else *chase->append() = span; #endif return last->segment(); } if (done) { continue; } if (!sortable) { continue; } // find first angle, initialize winding to computed wind sum const SkOpAngle* angle = segment->spanToAngle(*tIndex, *endIndex); const SkOpAngle* firstAngle; SkDEBUGCODE(firstAngle = angle); SkDEBUGCODE(bool loop = false); int winding; do { angle = angle->next(); SkASSERT(angle != firstAngle || !loop); SkDEBUGCODE(loop |= angle == firstAngle); segment = angle->segment(); winding = segment->windSum(angle); } while (winding == SK_MinS32); int spanWinding = segment->spanSign(angle->start(), angle->end()); #if DEBUG_WINDING SkDebugf("%s winding=%d spanWinding=%d\n", __FUNCTION__, winding, spanWinding); #endif // turn span winding into contour winding if (spanWinding * winding < 0) { winding += spanWinding; } // we care about first sign and whether wind sum indicates this // edge is inside or outside. Maybe need to pass span winding // or first winding or something into this function? // advance to first undone angle, then return it and winding // (to set whether edges are active or not) firstAngle = angle; winding -= firstAngle->segment()->spanSign(firstAngle); while ((angle = angle->next()) != firstAngle) { segment = angle->segment(); int maxWinding = winding; winding -= segment->spanSign(angle); #if DEBUG_SORT SkDebugf("%s id=%d maxWinding=%d winding=%d sign=%d\n", __FUNCTION__, segment->debugID(), maxWinding, winding, angle->sign()); #endif *tIndex = angle->start(); *endIndex = angle->end(); int lesser = SkMin32(*tIndex, *endIndex); const SkOpSpan& nextSpan = segment->span(lesser); if (!nextSpan.fDone) { // FIXME: this be wrong? assign startWinding if edge is in // same direction. If the direction is opposite, winding to // assign is flipped sign or +/- 1? if (SkOpSegment::UseInnerWinding(maxWinding, winding)) { maxWinding = winding; } // allowed to do nothing (void) segment->markAndChaseWinding(angle, maxWinding, 0, NULL); break; } } *chase->insert(0) = span; return segment; } return NULL; }
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; // keep track of subsequent returns to detect infinite loops SkTDArray<SortableTop> sortableTops; 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 (tryAgain) { bool giveUp = false; int count = sortableTops.count(); for (int index = 0; index < count; ++index) { const SortableTop& prev = sortableTops[index]; if (giveUp) { prev.fSegment->markDoneFinal(prev.fIndex); } else if (prev.fSegment == current && (prev.fIndex == *indexPtr || prev.fEndIndex == *endIndexPtr)) { // remaining edges are non-vertical and cannot have their winding computed // mark them as done and return, and hope that assembly can fill the holes giveUp = true; index = -1; } } if (giveUp) { *done = true; return NULL; } } SortableTop* sortableTop = sortableTops.append(); sortableTop->fSegment = current; sortableTop->fIndex = *indexPtr; sortableTop->fEndIndex = *endIndexPtr; #if DEBUG_SORT SkDebugf("%s current=%d index=%d endIndex=%d tHit=%1.9g hitDx=%1.9g try=%d vert=%d\n", __FUNCTION__, current->debugID(), *indexPtr, *endIndexPtr, tHit, hitDx, tryAgain, *onlyVertical); #endif 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); bool success = current->initWinding(*indexPtr, *endIndexPtr, tHit, contourWinding, hitDx, oppContourWinding, hitOppDx); if (current->done()) { return NULL; } else if (!success) { // check if the span has a valid winding int min = SkTMin(*indexPtr, *endIndexPtr); const SkOpSpan& span = current->span(min); if (span.fWindSum == SK_MinS32) { return NULL; } } return current; }
bool SkImageEncoder::encodeFile(const char file[], const SkBitmap& bm, int quality) { quality = SkMin32(100, SkMax32(0, quality)); SkFILEWStream stream(file); return this->onEncode(&stream, bm, quality); }
bool SkImageEncoder::encodeStream(SkWStream* stream, const SkBitmap& bm, int quality) { quality = SkMin32(100, SkMax32(0, quality)); return this->onEncode(stream, bm, quality); }
static bool bridgeOp(SkTArray<SkOpContour*, true>& contourList, const SkPathOp op, const int xorMask, const int xorOpMask, SkPathWriter* simple) { bool firstContour = true; bool unsortable = false; bool topUnsortable = false; SkPoint topLeft = {SK_ScalarMin, SK_ScalarMin}; do { int index, endIndex; bool done; SkOpSegment* current = FindSortableTop(contourList, SkOpAngle::kBinarySingle, &firstContour, &index, &endIndex, &topLeft, &topUnsortable, &done); if (!current) { if (topUnsortable || !done) { topUnsortable = false; SkASSERT(topLeft.fX != SK_ScalarMin && topLeft.fY != SK_ScalarMin); topLeft.fX = topLeft.fY = SK_ScalarMin; continue; } break; } SkTDArray<SkOpSpan*> chaseArray; do { if (current->activeOp(index, endIndex, xorMask, xorOpMask, op)) { do { if (!unsortable && current->done()) { #if DEBUG_ACTIVE_SPANS DebugShowActiveSpans(contourList); #endif if (simple->isEmpty()) { simple->init(); break; } } SkASSERT(unsortable || !current->done()); int nextStart = index; int nextEnd = endIndex; SkOpSegment* next = current->findNextOp(&chaseArray, &nextStart, &nextEnd, &unsortable, op, xorMask, xorOpMask); if (!next) { if (!unsortable && simple->hasMove() && current->verb() != SkPath::kLine_Verb && !simple->isClosed()) { current->addCurveTo(index, endIndex, simple, true); SkASSERT(simple->isClosed()); } break; } #if DEBUG_FLOW SkDebugf("%s current id=%d from=(%1.9g,%1.9g) to=(%1.9g,%1.9g)\n", __FUNCTION__, current->debugID(), current->xyAtT(index).fX, current->xyAtT(index).fY, current->xyAtT(endIndex).fX, current->xyAtT(endIndex).fY); #endif current->addCurveTo(index, endIndex, simple, true); current = next; index = nextStart; endIndex = nextEnd; } while (!simple->isClosed() && (!unsortable || !current->done(SkMin32(index, endIndex)))); if (current->activeWinding(index, endIndex) && !simple->isClosed()) { // FIXME : add to simplify, xor cpaths int min = SkMin32(index, endIndex); if (!unsortable && !simple->isEmpty()) { unsortable = current->checkSmall(min); } SkASSERT(unsortable || simple->isEmpty()); if (!current->done(min)) { current->addCurveTo(index, endIndex, simple, true); current->markDoneBinary(min); } } simple->close(); } else { SkOpSpan* last = current->markAndChaseDoneBinary(index, endIndex); if (last && !last->fLoop) { *chaseArray.append() = last; } } current = findChaseOp(chaseArray, index, endIndex); #if DEBUG_ACTIVE_SPANS DebugShowActiveSpans(contourList); #endif if (!current) { break; } } while (true); } while (true); return simple->someAssemblyRequired(); }
void SkScalerContext_FreeType_Base::generateGlyphImage(FT_Face face, const SkGlyph& glyph) { const bool doBGR = SkToBool(fRec.fFlags & SkScalerContext::kLCD_BGROrder_Flag); const bool doVert = SkToBool(fRec.fFlags & SkScalerContext::kLCD_Vertical_Flag); switch ( face->glyph->format ) { case FT_GLYPH_FORMAT_OUTLINE: { FT_Outline* outline = &face->glyph->outline; FT_BBox bbox; FT_Bitmap target; if (fRec.fFlags & SkScalerContext::kEmbolden_Flag) { emboldenOutline(face, outline); } int dx = 0, dy = 0; if (fRec.fFlags & SkScalerContext::kSubpixelPositioning_Flag) { dx = SkFixedToFDot6(glyph.getSubXFixed()); dy = SkFixedToFDot6(glyph.getSubYFixed()); // negate dy since freetype-y-goes-up and skia-y-goes-down dy = -dy; } FT_Outline_Get_CBox(outline, &bbox); /* what we really want to do for subpixel is offset(dx, dy) compute_bounds offset(bbox & !63) but that is two calls to offset, so we do the following, which achieves the same thing with only one offset call. */ FT_Outline_Translate(outline, dx - ((bbox.xMin + dx) & ~63), dy - ((bbox.yMin + dy) & ~63)); if (SkMask::kLCD16_Format == glyph.fMaskFormat) { FT_Render_Glyph(face->glyph, doVert ? FT_RENDER_MODE_LCD_V : FT_RENDER_MODE_LCD); if (fPreBlend.isApplicable()) { copyFT2LCD16<true>(glyph, face->glyph->bitmap, doBGR, doVert, fPreBlend.fR, fPreBlend.fG, fPreBlend.fB); } else { copyFT2LCD16<false>(glyph, face->glyph->bitmap, doBGR, doVert, fPreBlend.fR, fPreBlend.fG, fPreBlend.fB); } } else { target.width = glyph.fWidth; target.rows = glyph.fHeight; target.pitch = glyph.rowBytes(); target.buffer = reinterpret_cast<uint8_t*>(glyph.fImage); target.pixel_mode = compute_pixel_mode( (SkMask::Format)fRec.fMaskFormat); target.num_grays = 256; memset(glyph.fImage, 0, glyph.rowBytes() * glyph.fHeight); FT_Outline_Get_Bitmap(face->glyph->library, outline, &target); } } break; case FT_GLYPH_FORMAT_BITMAP: { if (fRec.fFlags & SkScalerContext::kEmbolden_Flag) { FT_GlyphSlot_Own_Bitmap(face->glyph); FT_Bitmap_Embolden(face->glyph->library, &face->glyph->bitmap, kBitmapEmboldenStrength, 0); } SkASSERT_CONTINUE(glyph.fWidth == face->glyph->bitmap.width); SkASSERT_CONTINUE(glyph.fHeight == face->glyph->bitmap.rows); SkASSERT_CONTINUE(glyph.fTop == -face->glyph->bitmap_top); SkASSERT_CONTINUE(glyph.fLeft == face->glyph->bitmap_left); const uint8_t* src = (const uint8_t*)face->glyph->bitmap.buffer; uint8_t* dst = (uint8_t*)glyph.fImage; if (face->glyph->bitmap.pixel_mode == FT_PIXEL_MODE_GRAY || (face->glyph->bitmap.pixel_mode == FT_PIXEL_MODE_MONO && glyph.fMaskFormat == SkMask::kBW_Format)) { unsigned srcRowBytes = face->glyph->bitmap.pitch; unsigned dstRowBytes = glyph.rowBytes(); unsigned minRowBytes = SkMin32(srcRowBytes, dstRowBytes); unsigned extraRowBytes = dstRowBytes - minRowBytes; for (int y = face->glyph->bitmap.rows - 1; y >= 0; --y) { memcpy(dst, src, minRowBytes); memset(dst + minRowBytes, 0, extraRowBytes); src += srcRowBytes; dst += dstRowBytes; } } else if (face->glyph->bitmap.pixel_mode == FT_PIXEL_MODE_MONO && glyph.fMaskFormat == SkMask::kA8_Format) { for (int y = 0; y < face->glyph->bitmap.rows; ++y) { uint8_t byte = 0; int bits = 0; const uint8_t* src_row = src; uint8_t* dst_row = dst; for (int x = 0; x < face->glyph->bitmap.width; ++x) { if (!bits) { byte = *src_row++; bits = 8; } *dst_row++ = byte & 0x80 ? 0xff : 0; bits--; byte <<= 1; } src += face->glyph->bitmap.pitch; dst += glyph.rowBytes(); } } else if (SkMask::kLCD16_Format == glyph.fMaskFormat) { if (fPreBlend.isApplicable()) { copyFT2LCD16<true>(glyph, face->glyph->bitmap, doBGR, doVert, fPreBlend.fR, fPreBlend.fG, fPreBlend.fB); } else { copyFT2LCD16<false>(glyph, face->glyph->bitmap, doBGR, doVert, fPreBlend.fR, fPreBlend.fG, fPreBlend.fB); } } else { SkDEBUGFAIL("unknown glyph bitmap transform needed"); } } break; default: SkDEBUGFAIL("unknown glyph format"); memset(glyph.fImage, 0, glyph.rowBytes() * glyph.fHeight); return; } // We used to always do this pre-USE_COLOR_LUMINANCE, but with colorlum, // it is optional #if defined(SK_GAMMA_APPLY_TO_A8) if (SkMask::kA8_Format == glyph.fMaskFormat && fPreBlend.isApplicable()) { uint8_t* SK_RESTRICT dst = (uint8_t*)glyph.fImage; unsigned rowBytes = glyph.rowBytes(); for (int y = glyph.fHeight - 1; y >= 0; --y) { for (int x = glyph.fWidth - 1; x >= 0; --x) { dst[x] = fPreBlend.fG[dst[x]]; } dst += rowBytes; } } #endif }
// This test hammers the GPU textblobcache and font atlas static void text_blob_cache_inner(skiatest::Reporter* reporter, GrContext* context, int maxTotalText, int maxGlyphID, int maxFamilies, bool normal, bool stressTest) { // setup surface uint32_t flags = 0; SkSurfaceProps props(flags, SkSurfaceProps::kLegacyFontHost_InitType); // configure our context for maximum stressing of cache and atlas if (stressTest) { GrTest::SetupAlwaysEvictAtlas(context); context->contextPriv().setTextBlobCacheLimit_ForTesting(0); } SkImageInfo info = SkImageInfo::Make(kWidth, kHeight, kN32_SkColorType, kPremul_SkAlphaType); auto surface(SkSurface::MakeRenderTarget(context, SkBudgeted::kNo, info, 0, &props)); REPORTER_ASSERT(reporter, surface); if (!surface) { return; } SkCanvas* canvas = surface->getCanvas(); sk_sp<SkFontMgr> fm(SkFontMgr::RefDefault()); int count = SkMin32(fm->countFamilies(), maxFamilies); // make a ton of text SkAutoTArray<uint16_t> text(maxTotalText); for (int i = 0; i < maxTotalText; i++) { text[i] = i % maxGlyphID; } // generate textblobs SkTArray<sk_sp<SkTextBlob>> blobs; for (int i = 0; i < count; i++) { SkPaint paint; paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); paint.setTextSize(48); // draw big glyphs to really stress the atlas SkString familyName; fm->getFamilyName(i, &familyName); sk_sp<SkFontStyleSet> set(fm->createStyleSet(i)); for (int j = 0; j < set->count(); ++j) { SkFontStyle fs; set->getStyle(j, &fs, nullptr); // We use a typeface which randomy returns unexpected mask formats to fuzz sk_sp<SkTypeface> orig(set->createTypeface(j)); if (normal) { paint.setTypeface(orig); } else { paint.setTypeface(sk_make_sp<SkRandomTypeface>(orig, paint, true)); } SkTextBlobBuilder builder; for (int aa = 0; aa < 2; aa++) { for (int subpixel = 0; subpixel < 2; subpixel++) { for (int lcd = 0; lcd < 2; lcd++) { paint.setAntiAlias(SkToBool(aa)); paint.setSubpixelText(SkToBool(subpixel)); paint.setLCDRenderText(SkToBool(lcd)); if (!SkToBool(lcd)) { paint.setTextSize(160); } const SkTextBlobBuilder::RunBuffer& run = builder.allocRun(paint, maxTotalText, 0, 0, nullptr); memcpy(run.glyphs, text.get(), maxTotalText * sizeof(uint16_t)); } } } blobs.emplace_back(builder.make()); } } // create surface where LCD is impossible info = SkImageInfo::MakeN32Premul(kWidth, kHeight); SkSurfaceProps propsNoLCD(0, kUnknown_SkPixelGeometry); auto surfaceNoLCD(canvas->makeSurface(info, &propsNoLCD)); REPORTER_ASSERT(reporter, surface); if (!surface) { return; } SkCanvas* canvasNoLCD = surfaceNoLCD->getCanvas(); // test redraw draw(canvas, 2, blobs); draw(canvasNoLCD, 2, blobs); // test draw after free context->freeGpuResources(); draw(canvas, 1, blobs); context->freeGpuResources(); draw(canvasNoLCD, 1, blobs); // test draw after abandon context->abandonContext(); draw(canvas, 1, blobs); }
// Returns true if this method handled the glyph, false if needs to be passed to fallback // bool GrDistanceFieldTextContext::appendGlyph(GrGlyph::PackedID packed, SkScalar sx, SkScalar sy, GrFontScaler* scaler) { if (NULL == fDrawTarget) { return true; } if (NULL == fStrike) { fStrike = fContext->getFontCache()->getStrike(scaler, true); } GrGlyph* glyph = fStrike->getGlyph(packed, scaler); if (NULL == glyph || glyph->fBounds.isEmpty()) { return true; } // fallback to color glyph support if (kA8_GrMaskFormat != glyph->fMaskFormat) { return false; } SkScalar dx = SkIntToScalar(glyph->fBounds.fLeft + SK_DistanceFieldInset); SkScalar dy = SkIntToScalar(glyph->fBounds.fTop + SK_DistanceFieldInset); SkScalar width = SkIntToScalar(glyph->fBounds.width() - 2*SK_DistanceFieldInset); SkScalar height = SkIntToScalar(glyph->fBounds.height() - 2*SK_DistanceFieldInset); SkScalar scale = fTextRatio; dx *= scale; dy *= scale; sx += dx; sy += dy; width *= scale; height *= scale; SkRect glyphRect = SkRect::MakeXYWH(sx, sy, width, height); // check if we clipped out SkRect dstRect; const SkMatrix& ctm = fViewMatrix; (void) ctm.mapRect(&dstRect, glyphRect); if (fClipRect.quickReject(SkScalarTruncToInt(dstRect.left()), SkScalarTruncToInt(dstRect.top()), SkScalarTruncToInt(dstRect.right()), SkScalarTruncToInt(dstRect.bottom()))) { return true; } if (NULL == glyph->fPlot) { // needs to be a separate conditional to avoid over-optimization // on Nexus 7 and Nexus 10 // If the glyph is too large we fall back to paths if (!uploadGlyph(glyph, scaler)) { if (NULL == glyph->fPath) { SkPath* path = SkNEW(SkPath); if (!scaler->getGlyphPath(glyph->glyphID(), path)) { // flag the glyph as being dead? delete path; return true; } glyph->fPath = path; } // flush any accumulated draws before drawing this glyph as a path. this->flush(); SkMatrix ctm; ctm.setScale(fTextRatio, fTextRatio); ctm.postTranslate(sx - dx, sy - dy); SkPath tmpPath(*glyph->fPath); tmpPath.transform(ctm); GrStrokeInfo strokeInfo(SkStrokeRec::kFill_InitStyle); fContext->drawPath(fRenderTarget, fClip, fPaint, fViewMatrix, tmpPath, strokeInfo); // remove this glyph from the vertices we need to allocate fTotalVertexCount -= kVerticesPerGlyph; return true; } } SkASSERT(glyph->fPlot); GrDrawTarget::DrawToken drawToken = fDrawTarget->getCurrentDrawToken(); glyph->fPlot->setDrawToken(drawToken); GrTexture* texture = glyph->fPlot->texture(); SkASSERT(texture); if (fCurrTexture != texture || fCurrVertex + kVerticesPerGlyph > fTotalVertexCount) { this->flush(); fCurrTexture = texture; fCurrTexture->ref(); } bool useColorVerts = !fUseLCDText; if (NULL == fVertices) { int maxQuadVertices = kVerticesPerGlyph * fContext->getQuadIndexBuffer()->maxQuads(); fAllocVertexCount = SkMin32(fTotalVertexCount, maxQuadVertices); fVertices = alloc_vertices(fDrawTarget, fAllocVertexCount, useColorVerts); } fVertexBounds.joinNonEmptyArg(glyphRect); int u0 = glyph->fAtlasLocation.fX + SK_DistanceFieldInset; int v0 = glyph->fAtlasLocation.fY + SK_DistanceFieldInset; int u1 = u0 + glyph->fBounds.width() - 2*SK_DistanceFieldInset; int v1 = v0 + glyph->fBounds.height() - 2*SK_DistanceFieldInset; size_t vertSize = get_vertex_stride(useColorVerts); intptr_t vertex = reinterpret_cast<intptr_t>(fVertices) + vertSize * fCurrVertex; // V0 SkPoint* position = reinterpret_cast<SkPoint*>(vertex); position->set(glyphRect.fLeft, glyphRect.fTop); if (useColorVerts) { SkColor* color = reinterpret_cast<SkColor*>(vertex + sizeof(SkPoint)); *color = fPaint.getColor(); } SkIPoint16* textureCoords = reinterpret_cast<SkIPoint16*>(vertex + vertSize - sizeof(SkIPoint16)); textureCoords->set(u0, v0); vertex += vertSize; // V1 position = reinterpret_cast<SkPoint*>(vertex); position->set(glyphRect.fLeft, glyphRect.fBottom); if (useColorVerts) { SkColor* color = reinterpret_cast<SkColor*>(vertex + sizeof(SkPoint)); *color = fPaint.getColor(); } textureCoords = reinterpret_cast<SkIPoint16*>(vertex + vertSize - sizeof(SkIPoint16)); textureCoords->set(u0, v1); vertex += vertSize; // V2 position = reinterpret_cast<SkPoint*>(vertex); position->set(glyphRect.fRight, glyphRect.fBottom); if (useColorVerts) { SkColor* color = reinterpret_cast<SkColor*>(vertex + sizeof(SkPoint)); *color = fPaint.getColor(); } textureCoords = reinterpret_cast<SkIPoint16*>(vertex + vertSize - sizeof(SkIPoint16)); textureCoords->set(u1, v1); vertex += vertSize; // V3 position = reinterpret_cast<SkPoint*>(vertex); position->set(glyphRect.fRight, glyphRect.fTop); if (useColorVerts) { SkColor* color = reinterpret_cast<SkColor*>(vertex + sizeof(SkPoint)); *color = fPaint.getColor(); } textureCoords = reinterpret_cast<SkIPoint16*>(vertex + vertSize - sizeof(SkIPoint16)); textureCoords->set(u1, v0); fCurrVertex += 4; return true; }
static void bridgeOp(SkTDArray<Contour*>& contourList, const ShapeOp op, const int aXorMask, const int bXorMask, PathWrapper& simple) { bool firstContour = true; SkPoint topLeft = {SK_ScalarMin, SK_ScalarMin}; do { #if SORTABLE_CONTOURS // old way Segment* topStart = findTopContour(contourList); if (!topStart) { break; } // Start at the top. Above the top is outside, below is inside. // follow edges to intersection by changing the index by direction. int index, endIndex; Segment* current = topStart->findTop(index, endIndex); #else // new way: iterate while top is unsortable int index, endIndex; Segment* current = findSortableTop(contourList, index, endIndex, topLeft); if (!current) { break; } #endif int contourWinding; if (firstContour) { contourWinding = 0; firstContour = false; } else { int sumWinding = current->windSum(SkMin32(index, endIndex)); // FIXME: don't I have to adjust windSum to get contourWinding? if (sumWinding == SK_MinS32) { sumWinding = current->computeSum(index, endIndex); } if (sumWinding == SK_MinS32) { contourWinding = innerContourCheck(contourList, current, index, endIndex); } else { contourWinding = sumWinding; int spanWinding = current->spanSign(index, endIndex); bool inner = useInnerWinding(sumWinding - spanWinding, sumWinding); if (inner) { contourWinding -= spanWinding; } #if DEBUG_WINDING SkDebugf("%s sumWinding=%d spanWinding=%d sign=%d inner=%d result=%d\n", __FUNCTION__, sumWinding, spanWinding, SkSign32(index - endIndex), inner, contourWinding); #endif } #if DEBUG_WINDING // SkASSERT(current->debugVerifyWinding(index, endIndex, contourWinding)); SkDebugf("%s contourWinding=%d\n", __FUNCTION__, contourWinding); #endif } // SkPoint lastPt; int winding = contourWinding; int spanWinding = current->spanSign(index, endIndex); int oppWinding = current->oppSign(index, endIndex); bool active = windingIsActive(winding, spanWinding, oppWinding, op); SkTDArray<Span*> chaseArray; bool unsortable = false; do { #if DEBUG_WINDING SkDebugf("%s active=%s winding=%d spanWinding=%d\n", __FUNCTION__, active ? "true" : "false", winding, spanWinding); #endif // const SkPoint* firstPt = NULL; do { SkASSERT(!current->done()); int nextStart = index; int nextEnd = endIndex; Segment* next = current->findNextOp(chaseArray, active, nextStart, nextEnd, winding, spanWinding, unsortable, op, aXorMask, bXorMask); if (!next) { // FIXME: if unsortable, allow partial paths to be later // assembled SkASSERT(!unsortable); if (active && simple.hasMove() && current->verb() != SkPath::kLine_Verb && !simple.isClosed()) { /* lastPt = */ current->addCurveTo(index, endIndex, simple, true); SkASSERT(simple.isClosed()); } break; } // if (!firstPt) { // firstPt = ¤t->addMoveTo(index, simple, active); // } /* lastPt = */ current->addCurveTo(index, endIndex, simple, active); current = next; index = nextStart; endIndex = nextEnd; } while (!simple.isClosed() && (active || !current->done())); if (simple.hasMove() && active) { #if DEBUG_PATH_CONSTRUCTION SkDebugf("%s close\n", __FUNCTION__); #endif simple.close(); } current = findChase(chaseArray, index, endIndex, contourWinding); #if DEBUG_ACTIVE_SPANS debugShowActiveSpans(contourList); #endif if (!current) { break; } int lesser = SkMin32(index, endIndex); spanWinding = current->spanSign(index, endIndex); winding = current->windSum(lesser); bool inner = useInnerWinding(winding - spanWinding, winding); #if DEBUG_WINDING SkDebugf("%s id=%d t=%1.9g spanWinding=%d winding=%d sign=%d" " inner=%d result=%d\n", __FUNCTION__, current->debugID(), current->t(lesser), spanWinding, winding, SkSign32(index - endIndex), useInnerWinding(winding - spanWinding, winding), inner ? winding - spanWinding : winding); #endif if (inner) { winding -= spanWinding; } int oppWinding = current->oppSign(index, endIndex); active = windingIsActive(winding, spanWinding, oppWinding, op); } while (true); } while (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 SkMagnifierImageFilter::onFilterImage(Proxy* 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; } SkAutoTUnref<SkBaseDevice> device(proxy->createDevice(src.width(), src.height())); if (!device) { return false; } *dst = device->accessBitmap(false); SkAutoLockPixels alp_dst(*dst); 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 = SkTPin(SkScalarFloorToInt(x_interp), 0, width - 1); int y_val = SkTPin(SkScalarFloorToInt(y_interp), 0, height - 1); *dptr = sptr[y_val * width + x_val]; dptr++; } } return true; }
void SkDumpCanvas::drawData(const void* data, size_t length) { // this->dump(kDrawData_Verb, NULL, "drawData(%d)", length); this->dump(kDrawData_Verb, NULL, "drawData(%d) %.*s", length, SkMin32(length, 64), data); }
SkRTree::Branch SkRTree::bulkLoad(SkTDArray<Branch>* branches, int level) { if (branches->count() == 1) { // Only one branch: it will be the root Branch out = (*branches)[0]; branches->rewind(); return out; } else { // We sort the whole list by y coordinates, if we are told to do so. // // We expect Webkit / Blink to give us a reasonable x,y order. // Avoiding this call resulted in a 17% win for recording with // negligible difference in playback speed. if (fSortWhenBulkLoading) { SkTQSort(branches->begin(), branches->end() - 1, RectLessY()); } int numBranches = branches->count() / fMaxChildren; int remainder = branches->count() % fMaxChildren; int newBranches = 0; if (0 != remainder) { ++numBranches; // If the remainder isn't enough to fill a node, we'll need to add fewer nodes to // some other branches to make up for it if (remainder >= fMinChildren) { remainder = 0; } else { remainder = fMinChildren - remainder; } } int numStrips = SkScalarCeilToInt(SkScalarSqrt(SkIntToScalar(numBranches) * SkScalarInvert(fAspectRatio))); int numTiles = SkScalarCeilToInt(SkIntToScalar(numBranches) / SkIntToScalar(numStrips)); int currentBranch = 0; for (int i = 0; i < numStrips; ++i) { // Once again, if we are told to do so, we sort by x. if (fSortWhenBulkLoading) { int begin = currentBranch; int end = currentBranch + numTiles * fMaxChildren - SkMin32(remainder, (fMaxChildren - fMinChildren) * numTiles); if (end > branches->count()) { end = branches->count(); } // Now we sort horizontal strips of rectangles by their x coords SkTQSort(branches->begin() + begin, branches->begin() + end - 1, RectLessX()); } for (int j = 0; j < numTiles && currentBranch < branches->count(); ++j) { int incrementBy = fMaxChildren; if (remainder != 0) { // if need be, omit some nodes to make up for remainder if (remainder <= fMaxChildren - fMinChildren) { incrementBy -= remainder; remainder = 0; } else { incrementBy = fMinChildren; remainder -= fMaxChildren - fMinChildren; } } Node* n = allocateNode(level); n->fNumChildren = 1; *n->child(0) = (*branches)[currentBranch]; Branch b; b.fBounds = (*branches)[currentBranch].fBounds; b.fChild.subtree = n; ++currentBranch; for (int k = 1; k < incrementBy && currentBranch < branches->count(); ++k) { b.fBounds.join((*branches)[currentBranch].fBounds); *n->child(k) = (*branches)[currentBranch]; ++n->fNumChildren; ++currentBranch; } (*branches)[newBranches] = b; ++newBranches; } } branches->setCount(newBranches); return this->bulkLoad(branches, level + 1); } }
/** * This function performs a box blur in X, of the given radius. If the * "transpose" parameter is true, it will transpose the pixels on write, * such that X and Y are swapped. Reads are always performed from contiguous * memory in X, for speed. The destination buffer (dst) must be at least * (width + leftRadius + rightRadius) * height bytes in size. * * This is what the inner loop looks like before unrolling, and with the two * cases broken out separately (width < diameter, width >= diameter): * * if (width < diameter) { * for (int x = 0; x < width; ++x) { * sum += *right++; * *dptr = (sum * scale + half) >> 24; * dptr += dst_x_stride; * } * for (int x = width; x < diameter; ++x) { * *dptr = (sum * scale + half) >> 24; * dptr += dst_x_stride; * } * for (int x = 0; x < width; ++x) { * *dptr = (sum * scale + half) >> 24; * sum -= *left++; * dptr += dst_x_stride; * } * } else { * for (int x = 0; x < diameter; ++x) { * sum += *right++; * *dptr = (sum * scale + half) >> 24; * dptr += dst_x_stride; * } * for (int x = diameter; x < width; ++x) { * sum += *right++; * *dptr = (sum * scale + half) >> 24; * sum -= *left++; * dptr += dst_x_stride; * } * for (int x = 0; x < diameter; ++x) { * *dptr = (sum * scale + half) >> 24; * sum -= *left++; * dptr += dst_x_stride; * } * } */ static int boxBlur(const uint8_t* src, int src_y_stride, uint8_t* dst, int leftRadius, int rightRadius, int width, int height, bool transpose) { int diameter = leftRadius + rightRadius; int kernelSize = diameter + 1; int border = SkMin32(width, diameter); uint32_t scale = (1 << 24) / kernelSize; int new_width = width + SkMax32(leftRadius, rightRadius) * 2; int dst_x_stride = transpose ? height : 1; int dst_y_stride = transpose ? 1 : new_width; uint32_t half = 1 << 23; for (int y = 0; y < height; ++y) { uint32_t sum = 0; uint8_t* dptr = dst + y * dst_y_stride; const uint8_t* right = src + y * src_y_stride; const uint8_t* left = right; for (int x = 0; x < rightRadius - leftRadius; x++) { *dptr = 0; dptr += dst_x_stride; } #define LEFT_BORDER_ITER \ sum += *right++; \ *dptr = (sum * scale + half) >> 24; \ dptr += dst_x_stride; int x = 0; #ifdef UNROLL_SEPARABLE_LOOPS for (; x < border - 16; x += 16) { LEFT_BORDER_ITER LEFT_BORDER_ITER LEFT_BORDER_ITER LEFT_BORDER_ITER LEFT_BORDER_ITER LEFT_BORDER_ITER LEFT_BORDER_ITER LEFT_BORDER_ITER LEFT_BORDER_ITER LEFT_BORDER_ITER LEFT_BORDER_ITER LEFT_BORDER_ITER LEFT_BORDER_ITER LEFT_BORDER_ITER LEFT_BORDER_ITER LEFT_BORDER_ITER } #endif for (; x < border; ++x) { LEFT_BORDER_ITER } #undef LEFT_BORDER_ITER #define TRIVIAL_ITER \ *dptr = (sum * scale + half) >> 24; \ dptr += dst_x_stride; x = width; #ifdef UNROLL_SEPARABLE_LOOPS for (; x < diameter - 16; x += 16) { TRIVIAL_ITER TRIVIAL_ITER TRIVIAL_ITER TRIVIAL_ITER TRIVIAL_ITER TRIVIAL_ITER TRIVIAL_ITER TRIVIAL_ITER TRIVIAL_ITER TRIVIAL_ITER TRIVIAL_ITER TRIVIAL_ITER TRIVIAL_ITER TRIVIAL_ITER TRIVIAL_ITER TRIVIAL_ITER } #endif for (; x < diameter; ++x) { TRIVIAL_ITER } #undef TRIVIAL_ITER #define CENTER_ITER \ sum += *right++; \ *dptr = (sum * scale + half) >> 24; \ sum -= *left++; \ dptr += dst_x_stride; x = diameter; #ifdef UNROLL_SEPARABLE_LOOPS for (; x < width - 16; x += 16) { CENTER_ITER CENTER_ITER CENTER_ITER CENTER_ITER CENTER_ITER CENTER_ITER CENTER_ITER CENTER_ITER CENTER_ITER CENTER_ITER CENTER_ITER CENTER_ITER CENTER_ITER CENTER_ITER CENTER_ITER CENTER_ITER } #endif for (; x < width; ++x) { CENTER_ITER } #undef CENTER_ITER #define RIGHT_BORDER_ITER \ *dptr = (sum * scale + half) >> 24; \ sum -= *left++; \ dptr += dst_x_stride; x = 0; #ifdef UNROLL_SEPARABLE_LOOPS for (; x < border - 16; x += 16) { RIGHT_BORDER_ITER RIGHT_BORDER_ITER RIGHT_BORDER_ITER RIGHT_BORDER_ITER RIGHT_BORDER_ITER RIGHT_BORDER_ITER RIGHT_BORDER_ITER RIGHT_BORDER_ITER RIGHT_BORDER_ITER RIGHT_BORDER_ITER RIGHT_BORDER_ITER RIGHT_BORDER_ITER RIGHT_BORDER_ITER RIGHT_BORDER_ITER RIGHT_BORDER_ITER RIGHT_BORDER_ITER } #endif for (; x < border; ++x) { RIGHT_BORDER_ITER } #undef RIGHT_BORDER_ITER for (int x = 0; x < leftRadius - rightRadius; ++x) { *dptr = 0; dptr += dst_x_stride; } SkASSERT(sum == 0); } return new_width; }
bool MCGBlurBox(const SkMask& p_src, SkScalar p_x_radius, SkScalar p_y_radius, SkScalar p_x_spread, SkScalar p_y_spread, SkMask& r_dst) { int t_pass_count; t_pass_count = 3; // Maximum amount of spread is 254 pixels. int x_spread, y_spread; x_spread = SkMin32(SkScalarFloor(p_x_radius * p_x_spread), 254); y_spread = SkMin32(SkScalarFloor(p_y_radius * p_y_spread), 254); p_x_radius -= x_spread; p_y_radius -= y_spread; int rx, ry; rx = SkScalarCeil(p_x_radius); ry = SkScalarCeil(p_y_radius); SkScalar px, py; px = rx; py = ry; int wx, wy; wx = 255 - SkScalarRound((SkIntToScalar(rx) - px) * 255); wy = 255 - SkScalarRound((SkIntToScalar(ry) - py) * 255); int t_pad_x, t_pad_y; t_pad_x = rx + x_spread; t_pad_y = ry + y_spread; r_dst . fBounds . set(p_src . fBounds . fLeft - t_pad_x, p_src . fBounds . fTop - t_pad_y, p_src . fBounds . fRight + t_pad_x, p_src . fBounds . fBottom + t_pad_y); r_dst . fRowBytes = r_dst . fBounds . width(); r_dst . fFormat = SkMask::kA8_Format; r_dst . fImage = NULL; if (p_src . fImage == NULL) return true; size_t t_dst_size; t_dst_size = r_dst . computeImageSize(); if (t_dst_size == 0) return false; int sw, sh; sw = p_src . fBounds . width(); sh = p_src . fBounds . height(); const uint8_t *sp; sp = p_src . fImage; uint8_t *dp; dp = SkMask::AllocImage(t_dst_size); if (dp == nil) return false; uint8_t *tp; tp = SkMask::AllocImage(t_dst_size); if (tp == nil) { SkMask::FreeImage(dp); return false; } int w, h; w = sw; h = sh; if (wx == 255) { if (t_pass_count == 3) { int r; r = rx; bool t_has_spread; t_has_spread = false; if (x_spread != 0 || y_spread != 0) { //w = dilateMask(sp, p_src . fRowBytes, tp, x_spread, w, h, true); //h = dilateMask(tp, h, dp, y_spread, h, w, true); dilateDistanceXY(sp, dp, x_spread, y_spread, w, h, w, h); t_has_spread = true; } int trx; trx = (r + 2) / 3; r -= trx; int rx_lo, rx_hi; rx_lo = rx_hi = trx; w = boxBlur(t_has_spread ? dp : sp, t_has_spread ? w : p_src . fRowBytes, tp, rx_lo, rx_hi, w, h, false); trx = (r + 1) / 2; r -= trx; rx_lo = rx_hi = trx; w = boxBlur(tp, w, dp, rx_hi, rx_lo, w, h, false); trx = r; rx_lo = rx_hi = trx; w = boxBlur(dp, w, tp, rx_hi, rx_hi, w, h, true); } else w = boxBlur(sp, p_src . fRowBytes, tp, rx, rx, w, h, true); } else { if (t_pass_count == 3) { int r; r = rx; bool t_has_spread; t_has_spread = false; if (x_spread != 0 || y_spread != 0) { //w = dilateMask(sp, p_src . fRowBytes, tp, x_spread, w, h, true); //h = dilateMask(tp, h, dp, y_spread, h, w, true); dilateDistanceXY(sp, dp, x_spread, y_spread, w, h, w, h); t_has_spread = true; } int trx; trx = (r + 2) / 3; r -= trx; w = boxBlurInterp(t_has_spread ? dp : sp, t_has_spread ? w : p_src . fRowBytes, tp, trx, w, h, false, wx); trx = (r + 1) / 2; r -= trx; w = boxBlurInterp(tp, w, dp, trx, w, h, false, wx); trx = r; w = boxBlurInterp(dp, w, tp, trx, w, h, true, wx); } else w = boxBlurInterp(sp, p_src . fRowBytes, tp, rx, w, h, true, wx); } if (wy == 255) { if (t_pass_count == 3) { int r; r = ry; int sry; sry = (r + 2) / 3; r -= sry; int ry_lo, ry_hi; ry_lo = ry_hi = sry; h = boxBlur(tp, h, dp, ry_lo, ry_hi, h, w, false); sry = (r + 1) / 2; r -= sry; ry_lo = ry_hi = sry; h = boxBlur(dp, h, tp, ry_hi, ry_lo, h, w, false); sry = r; ry_lo = ry_hi = sry; h = boxBlur(tp, h, dp, ry_hi, ry_hi, h, w, true); } else h = boxBlur(tp, h, dp, ry, ry, h, w, true); } else { if (t_pass_count == 3) { int r; r = ry; int sry; sry = (r + 2) / 3; r -= sry; h = boxBlurInterp(tp, h, dp, sry, h, w, false, wy); sry = (r + 1) / 2; r -= sry; h = boxBlurInterp(dp, h, tp, sry, h, w, false, wy); sry = r; h = boxBlurInterp(tp, h, dp, sry, h, w, true, wy); } else w = boxBlurInterp(tp, h, dp, rx, h, w, true, wy); } SkMask::FreeImage(tp); r_dst . fImage = dp; return true; }