SkRect LayerAndroid::subtractLayers(const SkRect& visibleRect) const { SkRect result; if (m_recordingPicture) { SkRect globalRect = bounds(); globalRect.offset(-getPosition()); // localToGlobal adds in position SkMatrix globalMatrix; localToGlobal(&globalMatrix); globalMatrix.mapRect(&globalRect); SkIRect roundedGlobal; globalRect.round(&roundedGlobal); SkIRect iVisibleRect; visibleRect.round(&iVisibleRect); SkRegion visRegion(iVisibleRect); visRegion.op(roundedGlobal, SkRegion::kDifference_Op); result.set(visRegion.getBounds()); #if DEBUG_NAV_UI SkDebugf("%s visibleRect=(%g,%g,r=%g,b=%g) globalRect=(%g,%g,r=%g,b=%g)" "result=(%g,%g,r=%g,b=%g)", __FUNCTION__, visibleRect.fLeft, visibleRect.fTop, visibleRect.fRight, visibleRect.fBottom, globalRect.fLeft, globalRect.fTop, globalRect.fRight, globalRect.fBottom, result.fLeft, result.fTop, result.fRight, result.fBottom); #endif } else result = visibleRect; for (int i = 0; i < countChildren(); i++) result = getChild(i)->subtractLayers(result); return result; }
void SkScan::FillRect(const SkRect& r, const SkRegion* clip, SkBlitter* blitter) { SkIRect ir; r.round(&ir); SkScan::FillIRect(ir, clip, blitter); }
static void test_cubic2() { const char* str = "M2242 -590088L-377758 9.94099e+07L-377758 9.94099e+07L2242 -590088Z"; SkPath path; SkParsePath::FromSVGString(str, &path); { #ifdef SK_BUILD_FOR_WIN // windows doesn't have strtof float x = (float)strtod("9.94099e+07", NULL); #else float x = strtof("9.94099e+07", NULL); #endif int ix = (int)x; int fx = (int)(x * 65536); int ffx = SkScalarToFixed(x); printf("%g %x %x %x\n", x, ix, fx, ffx); SkRect r = path.getBounds(); SkIRect ir; r.round(&ir); printf("[%g %g %g %g] [%x %x %x %x]\n", SkScalarToDouble(r.fLeft), SkScalarToDouble(r.fTop), SkScalarToDouble(r.fRight), SkScalarToDouble(r.fBottom), ir.fLeft, ir.fTop, ir.fRight, ir.fBottom); } SkBitmap bitmap; bitmap.setConfig(SkBitmap::kARGB_8888_Config, 300, 200); bitmap.allocPixels(); SkCanvas canvas(bitmap); SkPaint paint; paint.setAntiAlias(true); canvas.drawPath(path, paint); }
/** * For the purposes of drawing bitmaps, if a matrix is "almost" translate * go ahead and treat it as if it were, so that subsequent code can go fast. */ static bool just_trans_clamp(const SkMatrix& matrix, const SkPixmap& pixmap) { SkASSERT(matrix_only_scale_translate(matrix)); if (matrix.getType() & SkMatrix::kScale_Mask) { SkRect dst; SkRect src = SkRect::Make(pixmap.bounds()); // Can't call mapRect(), since that will fix up inverted rectangles, // e.g. when scale is negative, and we don't want to return true for // those. matrix.mapPoints(SkTCast<SkPoint*>(&dst), SkTCast<const SkPoint*>(&src), 2); // Now round all 4 edges to device space, and then compare the device // width/height to the original. Note: we must map all 4 and subtract // rather than map the "width" and compare, since we care about the // phase (in pixel space) that any translate in the matrix might impart. SkIRect idst; dst.round(&idst); return idst.width() == pixmap.width() && idst.height() == pixmap.height(); } // if we got here, we're either kTranslate_Mask or identity return true; }
// Draws the given bitmap to the given canvas. The subset of the source bitmap // identified by src_rect is drawn to the given destination rect. The bitmap // will be resampled to resample_width * resample_height (this is the size of // the whole image, not the subset). See shouldResampleBitmap for more. // // This does a lot of computation to resample only the portion of the bitmap // that will only be drawn. This is critical for performance since when we are // scrolling, for example, we are only drawing a small strip of the image. // Resampling the whole image every time is very slow, so this speeds up things // dramatically. // // Note: this code is only used when the canvas transformation is limited to // scaling or translation. static void drawResampledBitmap(SkCanvas& canvas, SkPaint& paint, const NativeImageSkia& bitmap, const SkIRect& srcIRect, const SkRect& destRect) { #if PLATFORM(CHROMIUM) TRACE_EVENT0("skia", "drawResampledBitmap"); #endif // Apply forward transform to destRect to estimate required size of // re-sampled bitmap, and use only in calls required to resize, or that // check for the required size. SkRect destRectTransformed; canvas.getTotalMatrix().mapRect(&destRectTransformed, destRect); SkIRect destRectTransformedRounded; destRectTransformed.round(&destRectTransformedRounded); // Compute the visible portion of our rect. SkRect destRectVisibleSubset; ClipRectToCanvas(canvas, destRect, &destRectVisibleSubset); // ClipRectToCanvas often overshoots, resulting in a larger region than our // original destRect. Intersecting gets us back inside. if (!destRectVisibleSubset.intersect(destRect)) return; // Nothing visible in destRect. // Compute the image-relative (bitmap space) subset. SkRect destBitmapSubset = destRectVisibleSubset; destBitmapSubset.offset(-destRect.x(), -destRect.y()); // Scale the subset to the requested size. The canvas scale can be negative, // but the resampling code is only interested in positive scaling in its normal space. SkMatrix subsetTransform; subsetTransform.setScale(SkScalarAbs(canvas.getTotalMatrix().getScaleX()), SkScalarAbs(canvas.getTotalMatrix().getScaleY())); SkRect destBitmapSubsetTransformed; subsetTransform.mapRect(&destBitmapSubsetTransformed, destBitmapSubset); SkIRect destBitmapSubsetTransformedRounded; destBitmapSubsetTransformed.round(&destBitmapSubsetTransformedRounded); // Transforms above plus rounding may cause destBitmapSubsetTransformedRounded // to go outside the image, so need to clip to avoid problems. if (!destBitmapSubsetTransformedRounded.intersect( 0, 0, destRectTransformedRounded.width(), destRectTransformedRounded.height())) return; // Image is not visible. SkBitmap resampled = bitmap.resizedBitmap(srcIRect, destRectTransformedRounded.width(), destRectTransformedRounded.height(), destBitmapSubsetTransformedRounded); canvas.drawBitmapRect(resampled, 0, destRectVisibleSubset, &paint); }
bool SkHitTestPath(const SkPath& path, SkRect& target, bool hires) { if (target.isEmpty()) { return false; } bool isInverse = path.isInverseFillType(); if (path.isEmpty()) { return isInverse; } SkRect bounds = path.getBounds(); bool sects = SkRect::Intersects(target, bounds); if (isInverse) { if (!sects) { return true; } } else { if (!sects) { return false; } if (target.contains(bounds)) { return true; } } SkPath devPath; const SkPath* pathPtr; SkRect devTarget; if (hires) { const SkScalar coordLimit = SkIntToScalar(16384); const SkRect limit = { 0, 0, coordLimit, coordLimit }; SkMatrix matrix; matrix.setRectToRect(bounds, limit, SkMatrix::kFill_ScaleToFit); path.transform(matrix, &devPath); matrix.mapRect(&devTarget, target); pathPtr = &devPath; } else { devTarget = target; pathPtr = &path; } SkIRect iTarget; devTarget.round(&iTarget); if (iTarget.isEmpty()) { iTarget.fLeft = SkScalarFloorToInt(devTarget.fLeft); iTarget.fTop = SkScalarFloorToInt(devTarget.fTop); iTarget.fRight = iTarget.fLeft + 1; iTarget.fBottom = iTarget.fTop + 1; } SkRegion clip(iTarget); SkRegion rgn; return rgn.setPath(*pathPtr, clip) ^ isInverse; }
void SkPageFlipper::inval(const SkRect& rect, bool antialias) { SkIRect r; rect.round(&r); if (antialias) { r.inset(-1, -1); } this->inval(r); }
static bool getClipBounds(JNIEnv* env, jobject, SkCanvas* canvas, jobject bounds) { SkRect r; SkIRect ir; bool result = canvas->getClipBounds(&r, SkCanvas::kBW_EdgeType); r.round(&ir); (void)GraphicsJNI::irect_to_jrect(ir, env, bounds); return result; }
// Draws the given bitmap to the given canvas. The subset of the source bitmap // identified by src_rect is drawn to the given destination rect. The bitmap // will be resampled to resample_width * resample_height (this is the size of // the whole image, not the subset). See shouldResampleBitmap for more. // // This does a lot of computation to resample only the portion of the bitmap // that will only be drawn. This is critical for performance since when we are // scrolling, for example, we are only drawing a small strip of the image. // Resampling the whole image every time is very slow, so this speeds up things // dramatically. // // Note: this code is only used when the canvas transformation is limited to // scaling or translation. static void drawResampledBitmap(SkCanvas& canvas, SkPaint& paint, const NativeImageSkia& bitmap, const SkIRect& srcIRect, const SkRect& destRect) { #if PLATFORM(CHROMIUM) TRACE_EVENT("drawResampledBitmap", &canvas, 0); #endif // Apply forward transform to destRect to estimate required size of // re-sampled bitmap, and use only in calls required to resize, or that // check for the required size. SkRect destRectTransformed; canvas.getTotalMatrix().mapRect(&destRectTransformed, destRect); SkIRect destRectTransformedRounded; destRectTransformed.round(&destRectTransformedRounded); // Compute the visible portion of our rect. SkRect destRectVisibleSubset; ClipRectToCanvas(canvas, destRect, &destRectVisibleSubset); // ClipRectToCanvas often overshoots, resulting in a larger region than our // original destRect. Intersecting gets us back inside. if (!destRectVisibleSubset.intersect(destRect)) return; // Nothing visible in destRect. // Compute the transformed (screen space) portion of the visible portion for // use below. SkRect destRectVisibleSubsetTransformed; canvas.getTotalMatrix().mapRect(&destRectVisibleSubsetTransformed, destRectVisibleSubset); SkRect destBitmapSubsetTransformed = destRectVisibleSubsetTransformed; destBitmapSubsetTransformed.offset(-destRectTransformed.fLeft, -destRectTransformed.fTop); SkIRect destBitmapSubsetTransformedRounded; destBitmapSubsetTransformed.round(&destBitmapSubsetTransformedRounded); // Transforms above plus rounding may cause destBitmapSubsetTransformedRounded // to go outside the image, so need to clip to avoid problems. if (!destBitmapSubsetTransformedRounded.intersect( 0, 0, destRectTransformedRounded.width(), destRectTransformedRounded.height())) return; // Image is not visible. SkBitmap resampled = bitmap.resizedBitmap(srcIRect, destRectTransformedRounded.width(), destRectTransformedRounded.height(), destBitmapSubsetTransformedRounded); canvas.drawBitmapRect(resampled, 0, destRectVisibleSubset, &paint); }
void drawRect(const SkRect& r, SkColor c) override { HDC hdc = (HDC)fCanvas->accessTopRasterHandle(); COLORREF cr = RGB(SkColorGetR(c), SkColorGetG(c), SkColorGetB(c));// SkEndian_Swap32(c) >> 8; FillRect(hdc, &toRECT(r.round()), CreateSolidBrush(cr)); // Assuming GDI wrote zeros for alpha, this will or-in 0xFF for alpha SkPaint paint; paint.setBlendMode(SkBlendMode::kDstATop); fCanvas->drawRect(r, paint); }
static jboolean getClipBounds(JNIEnv* env, jobject, jlong canvasHandle, jobject bounds) { SkRect r; SkIRect ir; bool result = get_canvas(canvasHandle)->getClipBounds(&r); if (!result) { r.setEmpty(); } r.round(&ir); (void)GraphicsJNI::irect_to_jrect(ir, env, bounds); return result ? JNI_TRUE : JNI_FALSE; }
bool SkRasterClip::op(const SkRect& localRect, const SkMatrix& matrix, const SkIRect& bounds, SkRegion::Op op, bool doAA) { AUTO_RASTERCLIP_VALIDATE(*this); SkRect devRect; if (fForceConservativeRects) { SkIRect ir; switch (mutate_conservative_op(&op, false)) { case kDoNothing_MutateResult: return !this->isEmpty(); case kReplaceClippedAgainstGlobalBounds_MutateResult: ir = bounds; break; case kContinue_MutateResult: matrix.mapRect(&devRect, localRect); ir = devRect.roundOut(); break; } return this->op(ir, op); } const bool isScaleTrans = matrix.isScaleTranslate(); if (!isScaleTrans) { SkPath path; path.addRect(localRect); path.setIsVolatile(true); return this->op(path, matrix, bounds, op, doAA); } matrix.mapRect(&devRect, localRect); if (fIsBW && doAA) { // check that the rect really needs aa, or is it close enought to // integer boundaries that we can just treat it as a BW rect? if (nearly_integral(devRect.fLeft) && nearly_integral(devRect.fTop) && nearly_integral(devRect.fRight) && nearly_integral(devRect.fBottom)) { doAA = false; } } if (fIsBW && !doAA) { SkIRect ir; devRect.round(&ir); (void)fBW.op(ir, op); } else { if (fIsBW) { this->convertToAA(); } (void)fAA.op(devRect, op, doAA); } return this->updateCacheAndReturnNonEmpty(); }
static inline void getSkiaBoundsForGlyph(SkPaint& paint, Glyph glyph, SkRect& bounds) { paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); SkPath path; paint.getTextPath(&glyph, sizeof(glyph), 0, 0, &path); bounds = path.getBounds(); if (!paint.isSubpixelText()) { SkIRect ir; bounds.round(&ir); bounds.set(ir); } }
void SkConservativeClip::opRect(const SkRect& localRect, const SkMatrix& ctm, const SkIRect& devBounds, SkRegion::Op op, bool doAA) { SkIRect ir; switch (mutate_conservative_op(&op, false)) { case kDoNothing_MutateResult: return; case kReplaceClippedAgainstGlobalBounds_MutateResult: ir = devBounds; break; case kContinue_MutateResult: { SkRect devRect; ctm.mapRect(&devRect, localRect); ir = doAA ? devRect.roundOut() : devRect.round(); } break; } this->opIRect(ir, op); }
bool SkRasterClip::op(const SkRect& r, SkRegion::Op op, bool doAA) { if (doAA) { // check that the rect really needs aa if (is_integral(r.fLeft) && is_integral(r.fTop) && is_integral(r.fRight) && is_integral(r.fBottom)) { doAA = false; } } if (fIsBW && !doAA) { SkIRect ir; r.round(&ir); return fBW.op(ir, op); } else { if (fIsBW) { this->convertToAA(); } return fAA.op(r, op, doAA); } }
FloatRect SimpleFontData::platformBoundsForGlyph(Glyph glyph) const { if (!m_platformData.size()) return FloatRect(); SkASSERT(sizeof(glyph) == 2); // compile-time assert SkPaint paint; m_platformData.setupPaint(&paint); paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); SkRect bounds; paint.measureText(&glyph, 2, &bounds); if (!paint.isSubpixelText()) { SkIRect ir; bounds.round(&ir); bounds.set(ir); } return FloatRect(bounds); }
void Canvas::drawImageNine(const CanvasImage* image, double centerLeft, double centerTop, double centerRight, double centerBottom, double dstLeft, double dstTop, double dstRight, double dstBottom, const Paint& paint) { if (!m_canvas) return; ASSERT(image); SkRect center = SkRect::MakeLTRB(centerLeft, centerTop, centerRight, centerBottom); SkIRect icenter; center.round(&icenter); m_canvas->drawImageNine(image->image(), icenter, SkRect::MakeLTRB(dstLeft, dstTop, dstRight, dstBottom), paint.paint()); }
bool SkWindow::handleInval(const SkRect* localR) { SkIRect ir; if (localR) { SkRect devR; SkMatrix inverse; if (!fMatrix.invert(&inverse)) { return false; } fMatrix.mapRect(&devR, *localR); devR.round(&ir); } else { ir.set(0, 0, SkScalarRoundToInt(this->width()), SkScalarRoundToInt(this->height())); } fDirtyRgn.op(ir, SkRegion::kUnion_Op); this->onHandleInval(ir); return true; }
bool SkRasterClip::op(const SkRect& r, const SkISize& size, SkRegion::Op op, bool doAA) { AUTO_RASTERCLIP_VALIDATE(*this); if (fForceConservativeRects) { SkIRect ir; switch (mutate_conservative_op(&op, false)) { case kDoNothing_MutateResult: return !this->isEmpty(); case kReplaceClippedAgainstGlobalBounds_MutateResult: ir = SkIRect::MakeSize(size); break; case kContinue_MutateResult: ir = r.roundOut(); break; } return this->op(ir, op); } if (fIsBW && doAA) { // check that the rect really needs aa, or is it close enought to // integer boundaries that we can just treat it as a BW rect? if (nearly_integral(r.fLeft) && nearly_integral(r.fTop) && nearly_integral(r.fRight) && nearly_integral(r.fBottom)) { doAA = false; } } if (fIsBW && !doAA) { SkIRect ir; r.round(&ir); (void)fBW.op(ir, op); } else { if (fIsBW) { this->convertToAA(); } (void)fAA.op(r, op, doAA); } return this->updateCacheAndReturnNonEmpty(); }
static void draw_rect(SkCanvas* canvas, const SkRect& r, SkColor c, SkColorProfileType profile, const SkAlpha aa[]) { const SkIRect ir = r.round(); SkBitmap bm; bm.allocN32Pixels(ir.width(), ir.height()); bm.eraseColor(0xFFFFFFFF); SkPixmap pm; bm.peekPixels(&pm); uint32_t flags = 0; if (SkColorGetA(c) == 0xFF) { flags |= SkXfermode::kSrcIsOpaque_PM4fFlag; } if (kSRGB_SkColorProfileType == profile) { flags |= SkXfermode::kDstIsSRGB_PM4fFlag; } const SkXfermode::PM4fState state { nullptr, flags }; const SkPM4f src = SkColor4f::FromColor(c).premul(); auto proc1 = SkXfermode::GetPM4fProc1(SkXfermode::kSrcOver_Mode, flags); for (int y = 0; y < ir.height()/2; ++y) { proc1(state, pm.writable_addr32(0, y), src, ir.width(), aa); } SkPM4f srcRow[1000]; for (int i = 0; i < ir.width(); ++i) { srcRow[i] = src; } auto procN = SkXfermode::GetPM4fProcN(SkXfermode::kSrcOver_Mode, flags); // +1 to skip a row, so we can see the boundary between proc1 and procN for (int y = ir.height()/2 + 1; y < ir.height(); ++y) { procN(state, pm.writable_addr32(0, y), srcRow, ir.width(), aa); } canvas->drawBitmap(bm, r.left(), r.top(), nullptr); }
bool SkRasterClip::op(const SkRect& localRect, const SkMatrix& matrix, const SkIRect& devBounds, SkRegion::Op op, bool doAA) { AUTO_RASTERCLIP_VALIDATE(*this); SkRect devRect; const bool isScaleTrans = matrix.isScaleTranslate(); if (!isScaleTrans) { SkPath path; path.addRect(localRect); path.setIsVolatile(true); return this->op(path, matrix, devBounds, op, doAA); } matrix.mapRect(&devRect, localRect); if (fIsBW && doAA) { // check that the rect really needs aa, or is it close enought to // integer boundaries that we can just treat it as a BW rect? if (nearly_integral(devRect.fLeft) && nearly_integral(devRect.fTop) && nearly_integral(devRect.fRight) && nearly_integral(devRect.fBottom)) { doAA = false; } } if (fIsBW && !doAA) { SkIRect ir; devRect.round(&ir); this->applyClipRestriction(op, &ir); (void)fBW.op(ir, op); } else { if (fIsBW) { this->convertToAA(); } this->applyClipRestriction(op, &devRect); (void)fAA.op(devRect, op, doAA); } return this->updateCacheAndReturnNonEmpty(); }
bool SkRasterClip::op(const SkRect& r, SkRegion::Op op, bool doAA) { AUTO_RASTERCLIP_VALIDATE(*this); if (fIsBW && doAA) { // check that the rect really needs aa, or is it close enought to // integer boundaries that we can just treat it as a BW rect? if (nearly_integral(r.fLeft) && nearly_integral(r.fTop) && nearly_integral(r.fRight) && nearly_integral(r.fBottom)) { doAA = false; } } if (fIsBW && !doAA) { SkIRect ir; r.round(&ir); (void)fBW.op(ir, op); } else { if (fIsBW) { this->convertToAA(); } (void)fAA.op(r, op, doAA); } return this->updateCacheAndReturnNonEmpty(); }
void NinePatch_Draw(SkCanvas* canvas, const SkRect& bounds, const SkBitmap& bitmap, const android::Res_png_9patch& chunk, const SkPaint* paint, SkRegion** outRegion) { if (canvas && canvas->quickReject(bounds, SkCanvas::kBW_EdgeType)) { return; } // if our canvas is GL, draw this as a mesh, which will be faster than // in parts (which is faster for raster) if (canvas && canvas->getViewport(NULL)) { SkNinePatch::DrawMesh(canvas, bounds, bitmap, chunk.xDivs, chunk.numXDivs, chunk.yDivs, chunk.numYDivs, paint); return; } #ifdef USE_TRACE gTrace = true; #endif SkASSERT(canvas || outRegion); #if 0 if (canvas) { const SkMatrix& m = canvas->getTotalMatrix(); SkDebugf("ninepatch [%g %g %g] [%g %g %g]\n", SkScalarToFloat(m[0]), SkScalarToFloat(m[1]), SkScalarToFloat(m[2]), SkScalarToFloat(m[3]), SkScalarToFloat(m[4]), SkScalarToFloat(m[5])); } #endif #ifdef USE_TRACE if (gTrace) { SkDEBUGF(("======== ninepatch bounds [%g %g]\n", SkScalarToFloat(bounds.width()), SkScalarToFloat(bounds.height()))); SkDEBUGF(("======== ninepatch paint bm [%d,%d]\n", bitmap.width(), bitmap.height())); SkDEBUGF(("======== ninepatch xDivs [%d,%d]\n", chunk.xDivs[0], chunk.xDivs[1])); SkDEBUGF(("======== ninepatch yDivs [%d,%d]\n", chunk.yDivs[0], chunk.yDivs[1])); } #endif if (bounds.isEmpty() || bitmap.width() == 0 || bitmap.height() == 0 || (paint && paint->getXfermode() == NULL && paint->getAlpha() == 0)) { #ifdef USE_TRACE if (gTrace) SkDEBUGF(("======== abort ninepatch draw\n")); #endif return; } // should try a quick-reject test before calling lockPixels SkAutoLockPixels alp(bitmap); // after the lock, it is valid to check getPixels() if (bitmap.getPixels() == NULL) return; SkPaint defaultPaint; if (NULL == paint) { paint = &defaultPaint; } const bool hasXfer = paint->getXfermode() != NULL; SkRect dst; SkIRect src; const int32_t x0 = chunk.xDivs[0]; const int32_t y0 = chunk.yDivs[0]; const SkColor initColor = ((SkPaint*)paint)->getColor(); const uint8_t numXDivs = chunk.numXDivs; const uint8_t numYDivs = chunk.numYDivs; int i; int j; int colorIndex = 0; uint32_t color; bool xIsStretchable; const bool initialXIsStretchable = (x0 == 0); bool yIsStretchable = (y0 == 0); const int bitmapWidth = bitmap.width(); const int bitmapHeight = bitmap.height(); SkScalar* dstRights = (SkScalar*) alloca((numXDivs + 1) * sizeof(SkScalar)); bool dstRightsHaveBeenCached = false; int numStretchyXPixelsRemaining = 0; for (i = 0; i < numXDivs; i += 2) { numStretchyXPixelsRemaining += chunk.xDivs[i + 1] - chunk.xDivs[i]; } int numFixedXPixelsRemaining = bitmapWidth - numStretchyXPixelsRemaining; int numStretchyYPixelsRemaining = 0; for (i = 0; i < numYDivs; i += 2) { numStretchyYPixelsRemaining += chunk.yDivs[i + 1] - chunk.yDivs[i]; } int numFixedYPixelsRemaining = bitmapHeight - numStretchyYPixelsRemaining; #if 0 SkDebugf("NinePatch [%d %d] bounds [%g %g %g %g] divs [%d %d]\n", bitmap.width(), bitmap.height(), SkScalarToFloat(bounds.fLeft), SkScalarToFloat(bounds.fTop), SkScalarToFloat(bounds.width()), SkScalarToFloat(bounds.height()), numXDivs, numYDivs); #endif src.fTop = 0; dst.fTop = bounds.fTop; // The first row always starts with the top being at y=0 and the bottom // being either yDivs[1] (if yDivs[0]=0) of yDivs[0]. In the former case // the first row is stretchable along the Y axis, otherwise it is fixed. // The last row always ends with the bottom being bitmap.height and the top // being either yDivs[numYDivs-2] (if yDivs[numYDivs-1]=bitmap.height) or // yDivs[numYDivs-1]. In the former case the last row is stretchable along // the Y axis, otherwise it is fixed. // // The first and last columns are similarly treated with respect to the X // axis. // // The above is to help explain some of the special casing that goes on the // code below. // The initial yDiv and whether the first row is considered stretchable or // not depends on whether yDiv[0] was zero or not. for (j = yIsStretchable ? 1 : 0; j <= numYDivs && src.fTop < bitmapHeight; j++, yIsStretchable = !yIsStretchable) { src.fLeft = 0; dst.fLeft = bounds.fLeft; if (j == numYDivs) { src.fBottom = bitmapHeight; dst.fBottom = bounds.fBottom; } else { src.fBottom = chunk.yDivs[j]; const int srcYSize = src.fBottom - src.fTop; if (yIsStretchable) { dst.fBottom = dst.fTop + calculateStretch(bounds.fBottom, dst.fTop, srcYSize, numStretchyYPixelsRemaining, numFixedYPixelsRemaining); numStretchyYPixelsRemaining -= srcYSize; } else { dst.fBottom = dst.fTop + SkIntToScalar(srcYSize); numFixedYPixelsRemaining -= srcYSize; } } xIsStretchable = initialXIsStretchable; // The initial xDiv and whether the first column is considered // stretchable or not depends on whether xDiv[0] was zero or not. for (i = xIsStretchable ? 1 : 0; i <= numXDivs && src.fLeft < bitmapWidth; i++, xIsStretchable = !xIsStretchable) { color = chunk.colors[colorIndex++]; if (i == numXDivs) { src.fRight = bitmapWidth; dst.fRight = bounds.fRight; } else { src.fRight = chunk.xDivs[i]; if (dstRightsHaveBeenCached) { dst.fRight = dstRights[i]; } else { const int srcXSize = src.fRight - src.fLeft; if (xIsStretchable) { dst.fRight = dst.fLeft + calculateStretch(bounds.fRight, dst.fLeft, srcXSize, numStretchyXPixelsRemaining, numFixedXPixelsRemaining); numStretchyXPixelsRemaining -= srcXSize; } else { dst.fRight = dst.fLeft + SkIntToScalar(srcXSize); numFixedXPixelsRemaining -= srcXSize; } dstRights[i] = dst.fRight; } } // If this horizontal patch is too small to be displayed, leave // the destination left edge where it is and go on to the next patch // in the source. if (src.fLeft >= src.fRight) { src.fLeft = src.fRight; continue; } // Make sure that we actually have room to draw any bits if (dst.fRight <= dst.fLeft || dst.fBottom <= dst.fTop) { goto nextDiv; } // If this patch is transparent, skip and don't draw. if (color == android::Res_png_9patch::TRANSPARENT_COLOR && !hasXfer) { if (outRegion) { if (*outRegion == NULL) { *outRegion = new SkRegion(); } SkIRect idst; dst.round(&idst); //LOGI("Adding trans rect: (%d,%d)-(%d,%d)\n", // idst.fLeft, idst.fTop, idst.fRight, idst.fBottom); (*outRegion)->op(idst, SkRegion::kUnion_Op); } goto nextDiv; } if (canvas) { #if 0 SkDebugf("-- src [%d %d %d %d] dst [%g %g %g %g]\n", src.fLeft, src.fTop, src.width(), src.height(), SkScalarToFloat(dst.fLeft), SkScalarToFloat(dst.fTop), SkScalarToFloat(dst.width()), SkScalarToFloat(dst.height())); if (2 == src.width() && SkIntToScalar(5) == dst.width()) { SkDebugf("--- skip patch\n"); } #endif drawStretchyPatch(canvas, src, dst, bitmap, *paint, initColor, color, hasXfer); } nextDiv: src.fLeft = src.fRight; dst.fLeft = dst.fRight; } src.fTop = src.fBottom; dst.fTop = dst.fBottom; dstRightsHaveBeenCached = true; } }
void SkGLDevice::drawVertices(const SkDraw& draw, SkCanvas::VertexMode vmode, int vertexCount, const SkPoint vertices[], const SkPoint texs[], const SkColor colors[], SkXfermode* xmode, const uint16_t indices[], int indexCount, const SkPaint& paint) { if (false) { SkRect bounds; SkIRect ibounds; bounds.set(vertices, vertexCount); bounds.round(&ibounds); SkDebugf("---- drawverts: %d pts, texs=%d colors=%d indices=%d bounds [%d %d]\n", vertexCount, texs!=0, colors!=0, indexCount, ibounds.width(), ibounds.height()); } SkGLClipIter* iter = this->updateMatrixClip(); SkGL::SetPaint(paint); const SkGLVertex* glVerts; const SkGLVertex* glTexs = NULL; #if GLSCALAR_IS_SCALAR glVerts = (const SkGLVertex*)vertices; #else SkAutoSTMalloc<32, SkGLVertex> storage(vertexCount); storage.get()->setPoints(vertices, vertexCount); glVerts = storage.get(); #endif uint8_t* colorArray = NULL; if (colors) { colorArray = (uint8_t*)sk_malloc_throw(vertexCount*4); SkGL::SetRGBA(colorArray, colors, vertexCount); } SkAutoFree afca(colorArray); SkGLVertex* texArray = NULL; TexCache* cache = NULL; if (texs && paint.getShader()) { SkShader* shader = paint.getShader(); // if (!shader->setContext(this->accessBitmap(), paint, *draw.fMatrix)) { if (!shader->setContext(*draw.fBitmap, paint, *draw.fMatrix)) { goto DONE; } SkBitmap bitmap; SkMatrix matrix; SkShader::TileMode tileModes[2]; if (shader->asABitmap(&bitmap, &matrix, tileModes)) { SkPoint max; GLuint name; cache = SkGLDevice::LockTexCache(bitmap, &name, &max); if (NULL == cache) { return; } matrix.postScale(max.fX / bitmap.width(), max.fY / bitmap.height()); glMatrixMode(GL_TEXTURE); SkGL::LoadMatrix(matrix); glMatrixMode(GL_MODELVIEW); #if GLSCALAR_IS_SCALAR glTexs = (const SkGLVertex*)texs; #else texArray = (SkGLVertex*)sk_malloc_throw(vertexCount * sizeof(SkGLVertex)); texArray->setPoints(texs, vertexCount); glTexs = texArray; #endif SkGL::SetPaintAlpha(paint); SkGL::SetTexParams(paint.isFilterBitmap(), tileModes[0], tileModes[1]); } } DONE: SkAutoFree aftex(texArray); SkGL::DrawVertices(indices ? indexCount : vertexCount, gVertexModeToGL[vmode], glVerts, glTexs, colorArray, indices, iter); if (cache) { SkGLDevice::UnlockTexCache(cache); } }
void NinePatchGlue::NinePatch_Draw(SkCanvas* canvas, const SkRect& bounds, const BitmapGlue& bitmap, const Res_png_9patch& chunk, const PaintGlue* paint, RegionGlue* *outRegion) { if (canvas && canvas->quickReject(bounds, SkCanvas::kBW_EdgeType)) return; PaintGlue defaultPaint; if (NULL == paint) { // matches default dither in NinePatchDrawable.java. defaultPaint.setDither(true); paint = &defaultPaint; } // if our SkCanvas were back by GL we should enable this and draw this as // a mesh, which will be faster in most cases. if (false) { SkNinePatch::DrawMesh(canvas, bounds, bitmap, chunk.xDivs, chunk.numXDivs, chunk.yDivs, chunk.numYDivs, paint); return; } SkASSERT(canvas || outRegion); if (bounds.isEmpty() || bitmap.width() == 0 || bitmap.height() == 0 || (paint && paint->getXfermode() == NULL && paint->getAlpha() == 0)) { return; } // should try a quick-reject test before calling lockPixels SkAutoLockPixels alp(bitmap); // after the lock, it is valid to check getPixels() if (bitmap.getPixels() == NULL) return; const bool hasXfer = paint->getXfermode() != NULL; SkRect dst; SkIRect src; const int32_t x0 = chunk.xDivs[0]; const int32_t y0 = chunk.yDivs[0]; const SkColor initColor = ((SkPaint*)paint)->getColor(); const uint8_t numXDivs = chunk.numXDivs; const uint8_t numYDivs = chunk.numYDivs; int i; int j; int colorIndex = 0; uint32_t color; bool xIsStretchable; const bool initialXIsStretchable = (x0 == 0); bool yIsStretchable = (y0 == 0); const int bitmapWidth = bitmap.width(); const int bitmapHeight = bitmap.height(); SkScalar* dstRights = (SkScalar*) alloca((numXDivs + 1) * sizeof(SkScalar)); bool dstRightsHaveBeenCached = false; int numStretchyXPixelsRemaining = 0; for (i = 0; i < numXDivs; i += 2) { numStretchyXPixelsRemaining += chunk.xDivs[i + 1] - chunk.xDivs[i]; } int numFixedXPixelsRemaining = bitmapWidth - numStretchyXPixelsRemaining; int numStretchyYPixelsRemaining = 0; for (i = 0; i < numYDivs; i += 2) { numStretchyYPixelsRemaining += chunk.yDivs[i + 1] - chunk.yDivs[i]; } int numFixedYPixelsRemaining = bitmapHeight - numStretchyYPixelsRemaining; src.fTop = 0; dst.fTop = bounds.fTop; // The first row always starts with the top being at y=0 and the bottom // being either yDivs[1] (if yDivs[0]=0) of yDivs[0]. In the former case // the first row is stretchable along the Y axis, otherwise it is fixed. // The last row always ends with the bottom being bitmap.height and the top // being either yDivs[numYDivs-2] (if yDivs[numYDivs-1]=bitmap.height) or // yDivs[numYDivs-1]. In the former case the last row is stretchable along // the Y axis, otherwise it is fixed. // // The first and last columns are similarly treated with respect to the X // axis. // // The above is to help explain some of the special casing that goes on the // code below. // The initial yDiv and whether the first row is considered stretchable or // not depends on whether yDiv[0] was zero or not. for (j = yIsStretchable ? 1 : 0; j <= numYDivs && src.fTop < bitmapHeight; j++, yIsStretchable = !yIsStretchable) { src.fLeft = 0; dst.fLeft = bounds.fLeft; if (j == numYDivs) { src.fBottom = bitmapHeight; dst.fBottom = bounds.fBottom; } else { src.fBottom = chunk.yDivs[j]; const int srcYSize = src.fBottom - src.fTop; if (yIsStretchable) { dst.fBottom = dst.fTop + calculateStretch(bounds.fBottom, dst.fTop, srcYSize, numStretchyYPixelsRemaining, numFixedYPixelsRemaining); numStretchyYPixelsRemaining -= srcYSize; } else { dst.fBottom = dst.fTop + SkIntToScalar(srcYSize); numFixedYPixelsRemaining -= srcYSize; } } xIsStretchable = initialXIsStretchable; // The initial xDiv and whether the first column is considered // stretchable or not depends on whether xDiv[0] was zero or not. for (i = xIsStretchable ? 1 : 0; i <= numXDivs && src.fLeft < bitmapWidth; i++, xIsStretchable = !xIsStretchable) { color = chunk.colors[colorIndex++]; if (i == numXDivs) { src.fRight = bitmapWidth; dst.fRight = bounds.fRight; } else { src.fRight = chunk.xDivs[i]; if (dstRightsHaveBeenCached) { dst.fRight = dstRights[i]; } else { const int srcXSize = src.fRight - src.fLeft; if (xIsStretchable) { dst.fRight = dst.fLeft + calculateStretch(bounds.fRight, dst.fLeft, srcXSize, numStretchyXPixelsRemaining, numFixedXPixelsRemaining); numStretchyXPixelsRemaining -= srcXSize; } else { dst.fRight = dst.fLeft + SkIntToScalar(srcXSize); numFixedXPixelsRemaining -= srcXSize; } dstRights[i] = dst.fRight; } } // If this horizontal patch is too small to be displayed, leave // the destination left edge where it is and go on to the next patch // in the source. if (src.fLeft >= src.fRight) { src.fLeft = src.fRight; continue; } // Make sure that we actually have room to draw any bits if (dst.fRight <= dst.fLeft || dst.fBottom <= dst.fTop) { goto nextDiv; } // If this patch is transparent, skip and don't draw. if (color == Res_png_9patch::TRANSPARENT_COLOR && !hasXfer) { if (outRegion) { if (*outRegion == NULL) { *outRegion = new RegionGlue(); } SkIRect idst; dst.round(&idst); //LOGI("Adding trans rect: (%d,%d)-(%d,%d)\n", // idst.fLeft, idst.fTop, idst.fRight, idst.fBottom); (*outRegion)->op(idst, SkRegion::kUnion_Op); } goto nextDiv; } if (canvas) { drawStretchyPatch(*canvas, src, dst, bitmap, *paint, initColor, color, hasXfer); } nextDiv: src.fLeft = src.fRight; dst.fLeft = dst.fRight; } src.fTop = src.fBottom; dst.fTop = dst.fBottom; dstRightsHaveBeenCached = true; } }
// Draws the given bitmap to the given canvas. The subset of the source bitmap // identified by src_rect is drawn to the given destination rect. The bitmap // will be resampled to resample_width * resample_height (this is the size of // the whole image, not the subset). See shouldResampleBitmap for more. // // This does a lot of computation to resample only the portion of the bitmap // that will only be drawn. This is critical for performance since when we are // scrolling, for example, we are only drawing a small strip of the image. // Resampling the whole image every time is very slow, so this speeds up things // dramatically. static void drawResampledBitmap(SkCanvas& canvas, SkPaint& paint, const NativeImageSkia& bitmap, const SkIRect& srcIRect, const SkRect& destRect) { // First get the subset we need. This is efficient and does not copy pixels. SkBitmap subset; bitmap.extractSubset(&subset, srcIRect); SkRect srcRect; srcRect.set(srcIRect); // Whether we're doing a subset or using the full source image. bool srcIsFull = srcIRect.fLeft == 0 && srcIRect.fTop == 0 && srcIRect.width() == bitmap.width() && srcIRect.height() == bitmap.height(); // We will always draw in integer sizes, so round the destination rect. SkIRect destRectRounded; destRect.round(&destRectRounded); SkIRect resizedImageRect = // Represents the size of the resized image. { 0, 0, destRectRounded.width(), destRectRounded.height() }; if (srcIsFull && bitmap.hasResizedBitmap(destRectRounded.width(), destRectRounded.height())) { // Yay, this bitmap frame already has a resized version. SkBitmap resampled = bitmap.resizedBitmap(destRectRounded.width(), destRectRounded.height()); canvas.drawBitmapRect(resampled, 0, destRect, &paint); return; } // Compute the visible portion of our rect. SkRect destBitmapSubsetSk; ClipRectToCanvas(canvas, destRect, &destBitmapSubsetSk); destBitmapSubsetSk.offset(-destRect.fLeft, -destRect.fTop); // The matrix inverting, etc. could have introduced rounding error which // causes the bounds to be outside of the resized bitmap. We round outward // so we always lean toward it being larger rather than smaller than we // need, and then clamp to the bitmap bounds so we don't get any invalid // data. SkIRect destBitmapSubsetSkI; destBitmapSubsetSk.roundOut(&destBitmapSubsetSkI); if (!destBitmapSubsetSkI.intersect(resizedImageRect)) return; // Resized image does not intersect. if (srcIsFull && bitmap.shouldCacheResampling( resizedImageRect.width(), resizedImageRect.height(), destBitmapSubsetSkI.width(), destBitmapSubsetSkI.height())) { // We're supposed to resize the entire image and cache it, even though // we don't need all of it. SkBitmap resampled = bitmap.resizedBitmap(destRectRounded.width(), destRectRounded.height()); canvas.drawBitmapRect(resampled, 0, destRect, &paint); } else { // We should only resize the exposed part of the bitmap to do the // minimal possible work. // Resample the needed part of the image. SkBitmap resampled = skia::ImageOperations::Resize(subset, skia::ImageOperations::RESIZE_LANCZOS3, destRectRounded.width(), destRectRounded.height(), destBitmapSubsetSkI); // Compute where the new bitmap should be drawn. Since our new bitmap // may be smaller than the original, we have to shift it over by the // same amount that we cut off the top and left. destBitmapSubsetSkI.offset(destRect.fLeft, destRect.fTop); SkRect offsetDestRect; offsetDestRect.set(destBitmapSubsetSkI); canvas.drawBitmapRect(resampled, 0, offsetDestRect, &paint); } }