void SkRecordedDrawable::flatten(SkWriteBuffer& buffer) const { // Write the bounds. buffer.writeRect(fBounds); // Create an SkPictureRecord to record the draw commands. SkPictInfo info; SkPictureRecord pictureRecord(SkISize::Make(fBounds.width(), fBounds.height()), 0); // If the query contains the whole picture, don't bother with the bounding box hierarchy. SkRect clipBounds; pictureRecord.getClipBounds(&clipBounds); SkBBoxHierarchy* bbh; if (clipBounds.contains(fBounds)) { bbh = nullptr; } else { bbh = fBBH.get(); } // Record the draw commands. pictureRecord.beginRecording(); SkRecordDraw(*fRecord, &pictureRecord, nullptr, fDrawableList->begin(), fDrawableList->count(), bbh, nullptr); pictureRecord.endRecording(); // Flatten the recorded commands and drawables. SkPictureData pictureData(pictureRecord, info); pictureData.flatten(buffer); }
// found and fixed for webkit: mishandling when we hit recursion limit on // mostly degenerate cubic flatness test DEF_TEST(Paint_regression_cubic, reporter) { SkPath path, stroke; SkPaint paint; path.moveTo(460.2881309415525f, 303.250847066498f); path.cubicTo(463.36378422175284f, 302.1169735073363f, 456.32239330810046f, 304.720354932878f, 453.15255460013304f, 305.788586869862f); SkRect fillR, strokeR; fillR = path.getBounds(); paint.setStyle(SkPaint::kStroke_Style); paint.setStrokeWidth(SkIntToScalar(2)); paint.getFillPath(path, &stroke); strokeR = stroke.getBounds(); SkRect maxR = fillR; SkScalar miter = SkMaxScalar(SK_Scalar1, paint.getStrokeMiter()); SkScalar inset = paint.getStrokeJoin() == SkPaint::kMiter_Join ? SkScalarMul(paint.getStrokeWidth(), miter) : paint.getStrokeWidth(); maxR.inset(-inset, -inset); // test that our stroke didn't explode REPORTER_ASSERT(reporter, maxR.contains(strokeR)); }
void OpaqueRegionSkia::markRectAsNonOpaque(const SkRect& rect) { // We want to keep as much of the current opaque rectangle as we can, so find the one largest // rectangle inside m_opaqueRect that does not intersect with |rect|. if (rect.contains(m_opaqueRect)) { m_opaqueRect.setEmpty(); return; } int deltaLeft = rect.fLeft - m_opaqueRect.fLeft; int deltaRight = m_opaqueRect.fRight - rect.fRight; int deltaTop = rect.fTop - m_opaqueRect.fTop; int deltaBottom = m_opaqueRect.fBottom - rect.fBottom; // horizontal is the larger of the two rectangles to the left or to the right of |rect| and inside m_opaqueRect. // vertical is the larger of the two rectangles above or below |rect| and inside m_opaqueRect. SkRect horizontal = m_opaqueRect; if (deltaTop > deltaBottom) horizontal.fBottom = rect.fTop; else horizontal.fTop = rect.fBottom; SkRect vertical = m_opaqueRect; if (deltaLeft > deltaRight) vertical.fRight = rect.fLeft; else vertical.fLeft = rect.fRight; if ((long)horizontal.width() * (long)horizontal.height() > (long)vertical.width() * (long)vertical.height()) m_opaqueRect = horizontal; else m_opaqueRect = vertical; }
void OpaqueRegionSkia::markRectAsOpaque(const SkRect& rect) { // We want to keep track of an opaque region but bound its complexity at a constant size. // We keep track of the largest rectangle seen by area. If we can add the new rect to this // rectangle then we do that, as that is the cheapest way to increase the area returned // without increasing the complexity. if (rect.isEmpty()) return; if (m_opaqueRect.contains(rect)) return; if (rect.contains(m_opaqueRect)) { m_opaqueRect = rect; return; } if (rect.fTop <= m_opaqueRect.fTop && rect.fBottom >= m_opaqueRect.fBottom) { if (rect.fLeft < m_opaqueRect.fLeft && rect.fRight >= m_opaqueRect.fLeft) m_opaqueRect.fLeft = rect.fLeft; if (rect.fRight > m_opaqueRect.fRight && rect.fLeft <= m_opaqueRect.fRight) m_opaqueRect.fRight = rect.fRight; } else if (rect.fLeft <= m_opaqueRect.fLeft && rect.fRight >= m_opaqueRect.fRight) { if (rect.fTop < m_opaqueRect.fTop && rect.fBottom >= m_opaqueRect.fTop) m_opaqueRect.fTop = rect.fTop; if (rect.fBottom > m_opaqueRect.fBottom && rect.fTop <= m_opaqueRect.fBottom) m_opaqueRect.fBottom = rect.fBottom; } long opaqueArea = (long)m_opaqueRect.width() * (long)m_opaqueRect.height(); long area = (long)rect.width() * (long)rect.height(); if (area > opaqueArea) m_opaqueRect = rect; }
bool check_bounds(const SkMatrix& viewMatrix, const SkRect& devBounds, void* vertices, int vCount) { SkRect tolDevBounds = devBounds; // The bounds ought to be tight, but in perspective the below code runs the verts // through the view matrix to get back to dev coords, which can introduce imprecision. if (viewMatrix.hasPerspective()) { tolDevBounds.outset(SK_Scalar1 / 1000, SK_Scalar1 / 1000); } else { // Non-persp matrices cause this path renderer to draw in device space. SkASSERT(viewMatrix.isIdentity()); } SkRect actualBounds; VertexType* verts = reinterpret_cast<VertexType*>(vertices); bool first = true; for (int i = 0; i < vCount; ++i) { SkPoint pos = verts[i].fPos; // This is a hack to workaround the fact that we move some degenerate segments offscreen. if (SK_ScalarMax == pos.fX) { continue; } viewMatrix.mapPoints(&pos, 1); if (first) { actualBounds.set(pos.fX, pos.fY, pos.fX, pos.fY); first = false; } else { actualBounds.growToInclude(pos.fX, pos.fY); } } if (!first) { return tolDevBounds.contains(actualBounds); } return true; }
virtual SkView::Click* onFindClickHandler(SkScalar x, SkScalar y, unsigned modi) override { for (size_t i = 0; i < SK_ARRAY_COUNT(fPts); ++i) { if (hittest(fPts[i], x, y)) { return new MyClick(this, (int)i); } } const SkRect& rectPt = SkRect::MakeXYWH(x, y, 1, 1); if (fWeightControl.contains(rectPt)) { return new MyClick(this, (int) SK_ARRAY_COUNT(fPts) + 1); } #ifdef SK_DEBUG if (fErrorControl.contains(rectPt)) { return new MyClick(this, (int) SK_ARRAY_COUNT(fPts) + 2); } #endif if (fWidthControl.contains(rectPt)) { return new MyClick(this, (int) SK_ARRAY_COUNT(fPts) + 3); } if (fCubicButton.fBounds.contains(rectPt)) { fCubicButton.fEnabled ^= true; return new MyClick(this, (int) SK_ARRAY_COUNT(fPts) + 4); } if (fConicButton.fBounds.contains(rectPt)) { fConicButton.fEnabled ^= true; return new MyClick(this, (int) SK_ARRAY_COUNT(fPts) + 5); } if (fQuadButton.fBounds.contains(rectPt)) { fQuadButton.fEnabled ^= true; return new MyClick(this, (int) SK_ARRAY_COUNT(fPts) + 6); } if (fRRectButton.fBounds.contains(rectPt)) { fRRectButton.fEnabled ^= true; return new MyClick(this, (int) SK_ARRAY_COUNT(fPts) + 7); } if (fCircleButton.fBounds.contains(rectPt)) { bool wasEnabled = fCircleButton.fEnabled; fCircleButton.fEnabled = !fCircleButton.fFill; fCircleButton.fFill = wasEnabled && !fCircleButton.fFill; return new MyClick(this, (int) SK_ARRAY_COUNT(fPts) + 8); } if (fTextButton.fBounds.contains(rectPt)) { fTextButton.fEnabled ^= true; return new MyClick(this, (int) SK_ARRAY_COUNT(fPts) + 9); } return this->INHERITED::onFindClickHandler(x, y, modi); }
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 SkGpuDevice::drawTextureProducer(GrTextureProducer* producer, const SkRect* srcRect, const SkRect* dstRect, SkCanvas::SrcRectConstraint constraint, const SkMatrix& viewMatrix, const GrClip& clip, const SkPaint& paint) { // This is the funnel for all non-tiled bitmap/image draw calls. Log a histogram entry. SK_HISTOGRAM_BOOLEAN("DrawTiled", false); // Figure out the actual dst and src rect by clipping the src rect to the bounds of the // adjuster. If the src rect is clipped then the dst rect must be recomputed. Also determine // the matrix that maps the src rect to the dst rect. SkRect clippedSrcRect; SkRect clippedDstRect; const SkRect srcBounds = SkRect::MakeIWH(producer->width(), producer->height()); SkMatrix srcToDstMatrix; if (srcRect) { if (!dstRect) { dstRect = &srcBounds; } if (!srcBounds.contains(*srcRect)) { clippedSrcRect = *srcRect; if (!clippedSrcRect.intersect(srcBounds)) { return; } if (!srcToDstMatrix.setRectToRect(*srcRect, *dstRect, SkMatrix::kFill_ScaleToFit)) { return; } srcToDstMatrix.mapRect(&clippedDstRect, clippedSrcRect); } else { clippedSrcRect = *srcRect; clippedDstRect = *dstRect; if (!srcToDstMatrix.setRectToRect(*srcRect, *dstRect, SkMatrix::kFill_ScaleToFit)) { return; } } } else { clippedSrcRect = srcBounds; if (dstRect) { clippedDstRect = *dstRect; if (!srcToDstMatrix.setRectToRect(srcBounds, *dstRect, SkMatrix::kFill_ScaleToFit)) { return; } } else { clippedDstRect = srcBounds; srcToDstMatrix.reset(); } } // Now that we have both the view and srcToDst matrices, log our scale factor. LogDrawScaleFactor(SkMatrix::Concat(viewMatrix, srcToDstMatrix), paint.getFilterQuality()); this->drawTextureProducerImpl(producer, clippedSrcRect, clippedDstRect, constraint, viewMatrix, srcToDstMatrix, clip, paint); }
void MediaTexture::draw(const TransformationMatrix& contentMatrix, const TransformationMatrix& videoMatrix, const SkRect& mediaBounds) { android::Mutex::Autolock lock(m_mediaLock); if (mediaBounds.isEmpty()) return; // draw all the video textures first for (unsigned int i = 0; i < m_videoTextures.size(); i++) { TextureWrapper* video = m_videoTextures[i]; if (!video->surfaceTexture.get() || video->dimensions.isEmpty() || !video->mediaListener->isFrameAvailable()) continue; video->surfaceTexture->updateTexImage(); float surfaceMatrix[16]; video->surfaceTexture->getTransformMatrix(surfaceMatrix); SkRect dimensions = video->dimensions; dimensions.offset(mediaBounds.fLeft, mediaBounds.fTop); #ifdef DEBUG if (!mediaBounds.contains(dimensions)) { ALOGV("The video exceeds is parent's bounds."); } #endif // DEBUG TilesManager::instance()->shader()->drawVideoLayerQuad(videoMatrix, surfaceMatrix, dimensions, video->textureId); } if (!m_contentTexture->mediaListener->isFrameAvailable()) return; m_contentTexture->surfaceTexture->updateTexImage(); sp<GraphicBuffer> buf = m_contentTexture->surfaceTexture->getCurrentBuffer(); PixelFormat f = buf->getPixelFormat(); // only attempt to use alpha blending if alpha channel exists bool forceAlphaBlending = !( PIXEL_FORMAT_RGBX_8888 == f || PIXEL_FORMAT_RGB_888 == f || PIXEL_FORMAT_RGB_565 == f); TextureQuadData data(m_contentTexture->textureId, GL_TEXTURE_EXTERNAL_OES, GL_LINEAR, LayerQuad, &contentMatrix, &mediaBounds, 1.0f, forceAlphaBlending); TilesManager::instance()->shader()->drawQuad(&data); }
bool SkPathContainsPoint(SkPath* originalPath, const FloatPoint& point, SkPath::FillType ft) { SkRegion rgn; SkRegion clip; SkPath::FillType originalFillType = originalPath->getFillType(); const SkPath* path = originalPath; SkPath scaledPath; int scale = 1; SkRect bounds; // FIXME: This #ifdef can go away once we're firmly using the new Skia. // During the transition, this makes the code compatible with both versions. #ifdef SK_USE_OLD_255_TO_256 bounds = originalPath->getBounds(); #else originalPath->computeBounds(&bounds, SkPath::kFast_BoundsType); #endif // We can immediately return false if the point is outside the bounding rect if (!bounds.contains(SkFloatToScalar(point.x()), SkFloatToScalar(point.y()))) return false; originalPath->setFillType(ft); // Skia has trouble with coordinates close to the max signed 16-bit values // If we have those, we need to scale. // // TODO: remove this code once Skia is patched to work properly with large // values const SkScalar kMaxCoordinate = SkIntToScalar(1<<15); SkScalar biggestCoord = std::max(std::max(std::max(bounds.fRight, bounds.fBottom), -bounds.fLeft), -bounds.fTop); if (biggestCoord > kMaxCoordinate) { scale = SkScalarCeil(SkScalarDiv(biggestCoord, kMaxCoordinate)); SkMatrix m; m.setScale(SkScalarInvert(SkIntToScalar(scale)), SkScalarInvert(SkIntToScalar(scale))); originalPath->transform(m, &scaledPath); path = &scaledPath; } int x = static_cast<int>(floorf(point.x() / scale)); int y = static_cast<int>(floorf(point.y() / scale)); clip.setRect(x, y, x + 1, y + 1); bool contains = rgn.setPath(*path, clip); originalPath->setFillType(originalFillType); return contains; }
const LayerAndroid* LayerAndroid::find(int x, int y) const { for (int i = 0; i < countChildren(); i++) { const LayerAndroid* found = getChild(i)->find(x, y); if (found) return found; } SkRect localBounds; bounds(&localBounds); if (localBounds.contains(x, y)) return this; return 0; }
void SkBigPicture::playback(SkCanvas* canvas, AbortCallback* callback) const { SkASSERT(canvas); // If the query contains the whole picture, don't bother with the BBH. SkRect clipBounds = { 0, 0, 0, 0 }; (void)canvas->getClipBounds(&clipBounds); const bool useBBH = !clipBounds.contains(this->cullRect()); SkRecordDraw(*fRecord, canvas, this->drawablePicts(), nullptr, this->drawableCount(), useBBH ? fBBH.get() : nullptr, callback); }
GrTextureDomain::GrTextureDomain(const SkRect& domain, Mode mode, int index) : fIndex(index) { static const SkRect kFullRect = {0, 0, SK_Scalar1, SK_Scalar1}; if (domain.contains(kFullRect) && kClamp_Mode == mode) { fMode = kIgnore_Mode; } else { fMode = mode; } if (fMode != kIgnore_Mode) { // We don't currently handle domains that are empty or don't intersect the texture. // It is OK if the domain rect is a line or point, but it should not be inverted. We do not // handle rects that do not intersect the [0..1]x[0..1] rect. SkASSERT(domain.fLeft <= domain.fRight); SkASSERT(domain.fTop <= domain.fBottom); fDomain.fLeft = SkScalarPin(domain.fLeft, kFullRect.fLeft, kFullRect.fRight); fDomain.fRight = SkScalarPin(domain.fRight, kFullRect.fLeft, kFullRect.fRight); fDomain.fTop = SkScalarPin(domain.fTop, kFullRect.fTop, kFullRect.fBottom); fDomain.fBottom = SkScalarPin(domain.fBottom, kFullRect.fTop, kFullRect.fBottom); SkASSERT(fDomain.fLeft <= fDomain.fRight); SkASSERT(fDomain.fTop <= fDomain.fBottom); } }
virtual bool onHitTest(SkScalar x, SkScalar y) { SkRect bounds; this->getBounds(&bounds); return bounds.contains(x, y); }
bool VideoLayerAndroid::drawGL() { // Lazily allocated the textures. if (!m_createdTexture) { m_backgroundTextureId = createBackgroundTexture(); m_spinnerOuterTextureId = createSpinnerOuterTexture(); m_spinnerInnerTextureId = createSpinnerInnerTexture(); m_posterTextureId = createPosterTexture(); m_createdTexture = true; } SkRect rect = SkRect::MakeSize(getSize()); GLfloat surfaceMatrix[16]; SkRect innerRect = SkRect(buttonRect); if (innerRect.contains(rect)) innerRect = rect; innerRect.offset((rect.width() - IMAGESIZE) / 2 , (rect.height() - IMAGESIZE) / 2); // Draw the poster image, the progressing image or the Video depending // on the player's state. if (m_playerState == PREPARING) { // Show the progressing animation, with two rotating circles // SSG if (m_surfaceTexture.get() && (m_surfaceTexture->getTimestamp() > 0)) { m_surfaceTexture->getTransformMatrix(surfaceMatrix); GLuint textureId = TilesManager::instance()->videoLayerManager()->getTextureId(uniqueId()); TilesManager::instance()->shader()->drawVideoLayerQuad(m_drawTransform, surfaceMatrix, rect, textureId); TilesManager::instance()->videoLayerManager()->updateMatrix(uniqueId(), surfaceMatrix); } else { TilesManager::instance()->shader()->drawLayerQuad(m_drawTransform, rect, m_backgroundTextureId, 1, true); } TransformationMatrix addReverseRotation; TransformationMatrix addRotation = m_drawTransform; addRotation.translate(innerRect.fLeft, innerRect.fTop); addRotation.translate(IMAGESIZE / 2, IMAGESIZE / 2); addReverseRotation = addRotation; addRotation.rotate(m_rotateDegree); addRotation.translate(-IMAGESIZE / 2, -IMAGESIZE / 2); SkRect size = SkRect::MakeWH(innerRect.width(), innerRect.height()); TilesManager::instance()->shader()->drawLayerQuad(addRotation, size, m_spinnerOuterTextureId, 1, true); addReverseRotation.rotate(-m_rotateDegree); addReverseRotation.translate(-IMAGESIZE / 2, -IMAGESIZE / 2); TilesManager::instance()->shader()->drawLayerQuad(addReverseRotation, size, m_spinnerInnerTextureId, 1, true); m_rotateDegree += ROTATESTEP; } else if (m_playerState == PLAYING && m_surfaceTexture.get()) { // Show the real video. m_surfaceTexture->updateTexImage(); m_surfaceTexture->getTransformMatrix(surfaceMatrix); GLuint textureId = TilesManager::instance()->videoLayerManager()->getTextureId(uniqueId()); TilesManager::instance()->shader()->drawVideoLayerQuad(m_drawTransform, surfaceMatrix, rect, textureId); TilesManager::instance()->videoLayerManager()->updateMatrix(uniqueId(), surfaceMatrix); } else { GLuint textureId = TilesManager::instance()->videoLayerManager()->getTextureId(uniqueId()); GLfloat* matrix = TilesManager::instance()->videoLayerManager()->getMatrix(uniqueId()); if (textureId && matrix) { // Show the screen shot for each video. TilesManager::instance()->shader()->drawVideoLayerQuad(m_drawTransform, matrix, rect, textureId); } else { // Show the static poster b/c there is no screen shot available. TilesManager::instance()->shader()->drawLayerQuad(m_drawTransform, rect, m_backgroundTextureId, 1, true); TilesManager::instance()->shader()->drawLayerQuad(m_drawTransform, innerRect, m_posterTextureId, 1, true); } } return drawChildrenGL(); }
// static bool SkBitmapScaler::Resize(SkBitmap* resultPtr, const SkBitmap& source, ResizeMethod method, float destWidth, float destHeight, SkBitmap::Allocator* allocator) { SkConvolutionProcs convolveProcs= { 0, NULL, NULL, NULL, NULL }; PlatformConvolutionProcs(&convolveProcs); SkRect destSubset = { 0, 0, destWidth, destHeight }; // Ensure that the ResizeMethod enumeration is sound. SkASSERT(((RESIZE_FIRST_QUALITY_METHOD <= method) && (method <= RESIZE_LAST_QUALITY_METHOD)) || ((RESIZE_FIRST_ALGORITHM_METHOD <= method) && (method <= RESIZE_LAST_ALGORITHM_METHOD))); SkRect dest = { 0, 0, destWidth, destHeight }; if (!dest.contains(destSubset)) { SkErrorInternals::SetError( kInvalidArgument_SkError, "Sorry, the destination bitmap scale subset " "falls outside the full destination bitmap." ); return false; } // If the size of source or destination is 0, i.e. 0x0, 0xN or Nx0, just // return empty. if (source.width() < 1 || source.height() < 1 || destWidth < 1 || destHeight < 1) { // todo: seems like we could handle negative dstWidth/Height, since that // is just a negative scale (flip) return false; } method = ResizeMethodToAlgorithmMethod(method); // Check that we deal with an "algorithm methods" from this point onward. SkASSERT((SkBitmapScaler::RESIZE_FIRST_ALGORITHM_METHOD <= method) && (method <= SkBitmapScaler::RESIZE_LAST_ALGORITHM_METHOD)); SkAutoLockPixels locker(source); if (!source.readyToDraw() || source.colorType() != kN32_SkColorType) { return false; } SkResizeFilter filter(method, source.width(), source.height(), destWidth, destHeight, destSubset, convolveProcs); // Get a source bitmap encompassing this touched area. We construct the // offsets and row strides such that it looks like a new bitmap, while // referring to the old data. const unsigned char* sourceSubset = reinterpret_cast<const unsigned char*>(source.getPixels()); // Convolve into the result. SkBitmap result; result.setInfo(SkImageInfo::MakeN32(SkScalarCeilToInt(destSubset.width()), SkScalarCeilToInt(destSubset.height()), source.alphaType())); result.allocPixels(allocator, NULL); if (!result.readyToDraw()) { return false; } BGRAConvolve2D(sourceSubset, static_cast<int>(source.rowBytes()), !source.isOpaque(), filter.xFilter(), filter.yFilter(), static_cast<int>(result.rowBytes()), static_cast<unsigned char*>(result.getPixels()), convolveProcs, true); *resultPtr = result; resultPtr->lockPixels(); SkASSERT(resultPtr->getPixels()); return true; }
void GrAtlasTextBatch::onPrepareDraws(Target* target) const { // if we have RGB, then we won't have any SkShaders so no need to use a localmatrix. // TODO actually only invert if we don't have RGBA SkMatrix localMatrix; if (this->usesLocalCoords() && !this->viewMatrix().invert(&localMatrix)) { SkDebugf("Cannot invert viewmatrix\n"); return; } GrTexture* texture = fFontCache->getTexture(this->maskFormat()); if (!texture) { SkDebugf("Could not allocate backing texture for atlas\n"); return; } GrMaskFormat maskFormat = this->maskFormat(); FlushInfo flushInfo; if (this->usesDistanceFields()) { flushInfo.fGeometryProcessor = this->setupDfProcessor(this->viewMatrix(), fFilteredColor, this->color(), texture); } else { GrTextureParams params(SkShader::kClamp_TileMode, GrTextureParams::kNone_FilterMode); flushInfo.fGeometryProcessor = GrBitmapTextGeoProc::Make(this->color(), texture, params, maskFormat, localMatrix, this->usesLocalCoords()); } flushInfo.fGlyphsToFlush = 0; size_t vertexStride = flushInfo.fGeometryProcessor->getVertexStride(); SkASSERT(vertexStride == GrAtlasTextBlob::GetVertexStride(maskFormat)); int glyphCount = this->numGlyphs(); const GrBuffer* vertexBuffer; void* vertices = target->makeVertexSpace(vertexStride, glyphCount * kVerticesPerGlyph, &vertexBuffer, &flushInfo.fVertexOffset); flushInfo.fVertexBuffer.reset(SkRef(vertexBuffer)); flushInfo.fIndexBuffer.reset(target->resourceProvider()->refQuadIndexBuffer()); if (!vertices || !flushInfo.fVertexBuffer) { SkDebugf("Could not allocate vertices\n"); return; } unsigned char* currVertex = reinterpret_cast<unsigned char*>(vertices); GrBlobRegenHelper helper(this, target, &flushInfo); SkAutoGlyphCache glyphCache; for (int i = 0; i < fGeoCount; i++) { const Geometry& args = fGeoData[i]; Blob* blob = args.fBlob; size_t byteCount; void* blobVertices; int subRunGlyphCount; blob->regenInBatch(target, fFontCache, &helper, args.fRun, args.fSubRun, &glyphCache, vertexStride, args.fViewMatrix, args.fX, args.fY, args.fColor, &blobVertices, &byteCount, &subRunGlyphCount); // now copy all vertices memcpy(currVertex, blobVertices, byteCount); #ifdef SK_DEBUG // bounds sanity check SkRect rect; rect.setLargestInverted(); SkPoint* vertex = (SkPoint*) ((char*)blobVertices); rect.growToInclude(vertex, vertexStride, kVerticesPerGlyph * subRunGlyphCount); if (this->usesDistanceFields()) { args.fViewMatrix.mapRect(&rect); } // Allow for small numerical error in the bounds. SkRect bounds = this->bounds(); bounds.outset(0.001f, 0.001f); SkASSERT(bounds.contains(rect)); #endif currVertex += byteCount; } this->flush(target, &flushInfo); }
/* //Seems a SAMSUNG change BEGIN bool VideoLayerAndroid::captureImage(PlatformWebGLVideoTexture* tex, VideoImageCallback callback) { if (m_playerState == PLAYING && m_surfaceTexture.get()) { Locker<WTF::Mutex> lock(m_mutexRef->mutex); bool ret = (tex->*callback)(m_surfaceTexture); glFinish(); return ret; } return false; } //Seems a SAMSUNG change END */ bool VideoLayerAndroid::drawGL(bool layerTilesDisabled) { // Lazily allocated the textures. TilesManager* tilesManager = TilesManager::instance(); VideoLayerManager* manager = tilesManager->videoLayerManager(); manager->initGLResourcesIfNeeded(); ShaderProgram* shader = tilesManager->shader(); SkRect rect = SkRect::MakeSize(getSize()); GLfloat surfaceMatrix[16]; // Calculate the video rect based on the aspect ratio and the element rect. SkRect videoRect = calVideoRect(rect); PureColorQuadData pureColorQuadData(Color(0, 0, 0, 255), LayerQuad, &m_drawTransform, &rect); if (videoRect != rect) { // Paint the whole video element with black color when video content // can't cover the whole area. shader->drawQuad(&pureColorQuadData); } // Inner rect is for the progressing / play / pause animation. SkRect innerRect = SkRect::MakeWH(manager->getButtonSize(), manager->getButtonSize()); if (innerRect.contains(videoRect)) innerRect = videoRect; double buttonSize = manager->getButtonSize(); innerRect.offset(videoRect.fLeft + (videoRect.width() - buttonSize) / 2, videoRect.fTop + (videoRect.height() - buttonSize) / 2); // When we are drawing the animation of the play/pause button in the // middle of the video, we need to ask for redraw. bool needRedraw = false; TextureQuadData iconQuadData(0, GL_TEXTURE_2D, GL_LINEAR, LayerQuad, &m_drawTransform, &innerRect); // Draw the poster image, the progressing image or the Video depending // on the player's state. if (m_playerState == PREPARING) { // Show the progressing animation, with two rotating circles showPreparingAnimation(videoRect, innerRect); needRedraw = true; } else if (m_playerState == PLAYING && m_surfaceTexture.get()) { // Show the real video. Locker<WTF::Mutex> lock(m_mutexRef->mutex);//Seems a SAMSUNG change m_surfaceTexture->updateTexImage(); m_surfaceTexture->getTransformMatrix(surfaceMatrix); GLuint textureId = manager->getTextureId(uniqueId()); shader->drawVideoLayerQuad(m_drawTransform, surfaceMatrix, videoRect, textureId); manager->updateMatrix(uniqueId(), surfaceMatrix); // Use the scale to control the fading the sizing during animation double scale = manager->drawIcon(uniqueId(), PlayIcon); if (scale) { innerRect.inset(manager->getButtonSize() / 4 * scale, manager->getButtonSize() / 4 * scale); iconQuadData.updateTextureId(manager->getPlayTextureId()); iconQuadData.updateOpacity(scale); shader->drawQuad(&iconQuadData); needRedraw = true; } glFinish(); //we can look probably at using a fence here //Seems a SAMSUNG change } else { GLuint textureId = manager->getTextureId(uniqueId()); GLfloat* matrix = manager->getMatrix(uniqueId()); if (textureId && matrix) { // Show the screen shot for each video. shader->drawVideoLayerQuad(m_drawTransform, matrix, videoRect, textureId); } else { // Show the static poster b/c there is no screen shot available. pureColorQuadData.updateColor(Color(128, 128, 128, 255)); shader->drawQuad(&pureColorQuadData); iconQuadData.updateTextureId(manager->getPosterTextureId()); iconQuadData.updateOpacity(1.0); shader->drawQuad(&iconQuadData); } // Use the scale to control the fading and the sizing during animation. double scale = manager->drawIcon(uniqueId(), PauseIcon); if (scale) { innerRect.inset(manager->getButtonSize() / 4 * scale, manager->getButtonSize() / 4 * scale); iconQuadData.updateTextureId(manager->getPauseTextureId()); iconQuadData.updateOpacity(scale); shader->drawQuad(&iconQuadData); needRedraw = true; } } return needRedraw; }
/** Determines whether a texture domain is necessary and if so what domain to use. There are two * rectangles to consider: * - The first is the content area specified by the texture adjuster (i.e., textureContentArea). * We can *never* allow filtering to cause bleed of pixels outside this rectangle. * - The second rectangle is the constraint rectangle (i.e., constraintRect), which is known to * be contained by the content area. The filterConstraint specifies whether we are allowed to * bleed across this rect. * * We want to avoid using a domain if possible. We consider the above rectangles, the filter type, * and whether the coords generated by the draw would all fall within the constraint rect. If the * latter is true we only need to consider whether the filter would extend beyond the rects. */ GrTextureProducer::DomainMode GrTextureProducer::DetermineDomainMode( const SkRect& constraintRect, FilterConstraint filterConstraint, bool coordsLimitedToConstraintRect, GrTextureProxy* proxy, const GrSamplerState::Filter* filterModeOrNullForBicubic, SkRect* domainRect) { const SkIRect proxyBounds = SkIRect::MakeWH(proxy->width(), proxy->height()); SkASSERT(proxyBounds.contains(constraintRect)); const bool proxyIsExact = GrProxyProvider::IsFunctionallyExact(proxy); // If the constraint rectangle contains the whole proxy then no need for a domain. if (constraintRect.contains(proxyBounds) && proxyIsExact) { return kNoDomain_DomainMode; } bool restrictFilterToRect = (filterConstraint == GrTextureProducer::kYes_FilterConstraint); // If we can filter outside the constraint rect, and there is no non-content area of the // proxy, and we aren't going to generate sample coords outside the constraint rect then we // don't need a domain. if (!restrictFilterToRect && proxyIsExact && coordsLimitedToConstraintRect) { return kNoDomain_DomainMode; } // Get the domain inset based on sampling mode (or bail if mipped) SkScalar filterHalfWidth = 0.f; if (filterModeOrNullForBicubic) { switch (*filterModeOrNullForBicubic) { case GrSamplerState::Filter::kNearest: if (coordsLimitedToConstraintRect) { return kNoDomain_DomainMode; } else { filterHalfWidth = 0.f; } break; case GrSamplerState::Filter::kBilerp: filterHalfWidth = .5f; break; case GrSamplerState::Filter::kMipMap: if (restrictFilterToRect || !proxyIsExact) { // No domain can save us here. return kTightCopy_DomainMode; } return kNoDomain_DomainMode; } } else { // bicubic does nearest filtering internally. filterHalfWidth = 1.5f; } // Both bilerp and bicubic use bilinear filtering and so need to be clamped to the center // of the edge texel. Pinning to the texel center has no impact on nearest mode and MIP-maps static const SkScalar kDomainInset = 0.5f; // Figure out the limits of pixels we're allowed to sample from. // Unless we know the amount of outset and the texture matrix we have to conservatively enforce // the domain. if (restrictFilterToRect) { *domainRect = constraintRect.makeInset(kDomainInset, kDomainInset); } else if (!proxyIsExact) { // If we got here then: proxy is not exact, the coords are limited to the // constraint rect, and we're allowed to filter across the constraint rect boundary. So // we check whether the filter would reach across the edge of the proxy. // We will only set the sides that are required. *domainRect = SkRectPriv::MakeLargest(); if (coordsLimitedToConstraintRect) { // We may be able to use the fact that the texture coords are limited to the constraint // rect in order to avoid having to add a domain. bool needContentAreaConstraint = false; if (proxyBounds.fRight - filterHalfWidth < constraintRect.fRight) { domainRect->fRight = proxyBounds.fRight - kDomainInset; needContentAreaConstraint = true; } if (proxyBounds.fBottom - filterHalfWidth < constraintRect.fBottom) { domainRect->fBottom = proxyBounds.fBottom - kDomainInset; needContentAreaConstraint = true; } if (!needContentAreaConstraint) { return kNoDomain_DomainMode; } } else { // Our sample coords for the texture are allowed to be outside the constraintRect so we // don't consider it when computing the domain. domainRect->fRight = proxyBounds.fRight - kDomainInset; domainRect->fBottom = proxyBounds.fBottom - kDomainInset; } } else { return kNoDomain_DomainMode; } if (domainRect->fLeft > domainRect->fRight) { domainRect->fLeft = domainRect->fRight = SkScalarAve(domainRect->fLeft, domainRect->fRight); } if (domainRect->fTop > domainRect->fBottom) { domainRect->fTop = domainRect->fBottom = SkScalarAve(domainRect->fTop, domainRect->fBottom); } return kDomain_DomainMode; }
static void testTightBoundsQuads(PathOpsThreadState* data) { SkRandom ran; const int bitWidth = 32; const int bitHeight = 32; const float pathMin = 1; const float pathMax = (float) (bitHeight - 2); SkBitmap& bits = *data->fBitmap; if (bits.width() == 0) { bits.allocN32Pixels(bitWidth, bitHeight); } SkCanvas canvas(bits); SkPaint paint; for (int index = 0; index < 100; ++index) { SkPath path; int contourCount = ran.nextRangeU(1, 10); for (int cIndex = 0; cIndex < contourCount; ++cIndex) { int lineCount = ran.nextRangeU(1, 10); path.moveTo(ran.nextRangeF(1, pathMax), ran.nextRangeF(pathMin, pathMax)); for (int lIndex = 0; lIndex < lineCount; ++lIndex) { if (ran.nextBool()) { path.lineTo(ran.nextRangeF(pathMin, pathMax), ran.nextRangeF(pathMin, pathMax)); } else { path.quadTo(ran.nextRangeF(pathMin, pathMax), ran.nextRangeF(pathMin, pathMax), ran.nextRangeF(pathMin, pathMax), ran.nextRangeF(pathMin, pathMax)); } } if (ran.nextBool()) { path.close(); } } SkRect classicBounds = path.getBounds(); SkRect tightBounds; REPORTER_ASSERT(data->fReporter, TightBounds(path, &tightBounds)); REPORTER_ASSERT(data->fReporter, classicBounds.contains(tightBounds)); canvas.drawColor(SK_ColorWHITE); canvas.drawPath(path, paint); SkIRect bitsWritten = {31, 31, 0, 0}; for (int y = 0; y < bitHeight; ++y) { uint32_t* addr1 = data->fBitmap->getAddr32(0, y); bool lineWritten = false; for (int x = 0; x < bitWidth; ++x) { if (addr1[x] == (uint32_t) -1) { continue; } lineWritten = true; bitsWritten.fLeft = SkTMin(bitsWritten.fLeft, x); bitsWritten.fRight = SkTMax(bitsWritten.fRight, x); } if (!lineWritten) { continue; } bitsWritten.fTop = SkTMin(bitsWritten.fTop, y); bitsWritten.fBottom = SkTMax(bitsWritten.fBottom, y); } if (!bitsWritten.isEmpty()) { SkIRect tightOut; tightBounds.roundOut(&tightOut); REPORTER_ASSERT(data->fReporter, tightOut.contains(bitsWritten)); } } }
/** Determines whether a texture domain is necessary and if so what domain to use. There are two * rectangles to consider: * - The first is the content area specified by the texture adjuster. We can *never* allow * filtering to cause bleed of pixels outside this rectangle. * - The second rectangle is the constraint rectangle, which is known to be contained by the * content area. The filterConstraint specifies whether we are allowed to bleed across this * rect. * * We want to avoid using a domain if possible. We consider the above rectangles, the filter type, * and whether the coords generated by the draw would all fall within the constraint rect. If the * latter is true we only need to consider whether the filter would extend beyond the rects. */ static DomainMode determine_domain_mode( const SkRect& constraintRect, GrTextureAdjuster::FilterConstraint filterConstraint, bool coordsLimitedToConstraintRect, int texW, int texH, const SkIRect* textureContentArea, const GrTextureParams::FilterMode* filterModeOrNullForBicubic, SkRect* domainRect) { SkASSERT(SkRect::MakeIWH(texW, texH).contains(constraintRect)); // We only expect a content area rect if there is some non-content area. SkASSERT(!textureContentArea || (!textureContentArea->contains(SkIRect::MakeWH(texW, texH)) && SkRect::Make(*textureContentArea).contains(constraintRect))); SkRect textureBounds = SkRect::MakeIWH(texW, texH); // If the src rectangle contains the whole texture then no need for a domain. if (constraintRect.contains(textureBounds)) { return kNoDomain_DomainMode; } bool restrictFilterToRect = (filterConstraint == GrTextureProducer::kYes_FilterConstraint); // If we can filter outside the constraint rect, and there is no non-content area of the // texture, and we aren't going to generate sample coords outside the constraint rect then we // don't need a domain. if (!restrictFilterToRect && !textureContentArea && coordsLimitedToConstraintRect) { return kNoDomain_DomainMode; } // Get the domain inset based on sampling mode (or bail if mipped) SkScalar filterHalfWidth = 0.f; if (filterModeOrNullForBicubic) { switch (*filterModeOrNullForBicubic) { case GrTextureParams::kNone_FilterMode: if (coordsLimitedToConstraintRect) { return kNoDomain_DomainMode; } else { filterHalfWidth = 0.f; } break; case GrTextureParams::kBilerp_FilterMode: filterHalfWidth = .5f; break; case GrTextureParams::kMipMap_FilterMode: if (restrictFilterToRect || textureContentArea) { // No domain can save us here. return kTightCopy_DomainMode; } return kNoDomain_DomainMode; } } else { // bicubic does nearest filtering internally. filterHalfWidth = 1.5f; } // Both bilerp and bicubic use bilinear filtering and so need to be clamped to the center // of the edge texel. Pinning to the texel center has no impact on nearest mode and MIP-maps static const SkScalar kDomainInset = 0.5f; // Figure out the limits of pixels we're allowed to sample from. // Unless we know the amount of outset and the texture matrix we have to conservatively enforce // the domain. if (restrictFilterToRect) { domainRect->fLeft = constraintRect.fLeft + kDomainInset; domainRect->fTop = constraintRect.fTop + kDomainInset; domainRect->fRight = constraintRect.fRight - kDomainInset; domainRect->fBottom = constraintRect.fBottom - kDomainInset; } else if (textureContentArea) { // If we got here then: there is a textureContentArea, the coords are limited to the // constraint rect, and we're allowed to filter across the constraint rect boundary. So // we check whether the filter would reach across the edge of the content area. // We will only set the sides that are required. domainRect->setLargest(); if (coordsLimitedToConstraintRect) { // We may be able to use the fact that the texture coords are limited to the constraint // rect in order to avoid having to add a domain. bool needContentAreaConstraint = false; if (textureContentArea->fLeft > 0 && textureContentArea->fLeft + filterHalfWidth > constraintRect.fLeft) { domainRect->fLeft = textureContentArea->fLeft + kDomainInset; needContentAreaConstraint = true; } if (textureContentArea->fTop > 0 && textureContentArea->fTop + filterHalfWidth > constraintRect.fTop) { domainRect->fTop = textureContentArea->fTop + kDomainInset; needContentAreaConstraint = true; } if (textureContentArea->fRight < texW && textureContentArea->fRight - filterHalfWidth < constraintRect.fRight) { domainRect->fRight = textureContentArea->fRight - kDomainInset; needContentAreaConstraint = true; } if (textureContentArea->fBottom < texH && textureContentArea->fBottom - filterHalfWidth < constraintRect.fBottom) { domainRect->fBottom = textureContentArea->fBottom - kDomainInset; needContentAreaConstraint = true; } if (!needContentAreaConstraint) { return kNoDomain_DomainMode; } } else { // Our sample coords for the texture are allowed to be outside the constraintRect so we // don't consider it when computing the domain. if (textureContentArea->fLeft != 0) { domainRect->fLeft = textureContentArea->fLeft + kDomainInset; } if (textureContentArea->fTop != 0) { domainRect->fTop = textureContentArea->fTop + kDomainInset; } if (textureContentArea->fRight != texW) { domainRect->fRight = textureContentArea->fRight - kDomainInset; } if (textureContentArea->fBottom != texH) { domainRect->fBottom = textureContentArea->fBottom - kDomainInset; } } } else { return kNoDomain_DomainMode; } if (domainRect->fLeft > domainRect->fRight) { domainRect->fLeft = domainRect->fRight = SkScalarAve(domainRect->fLeft, domainRect->fRight); } if (domainRect->fTop > domainRect->fBottom) { domainRect->fTop = domainRect->fBottom = SkScalarAve(domainRect->fTop, domainRect->fBottom); } domainRect->fLeft /= texW; domainRect->fTop /= texH; domainRect->fRight /= texW; domainRect->fBottom /= texH; return kDomain_DomainMode; }
// copied from FrameBuilder::deferShadow void EndReorderBarrierDrawable::drawShadow(SkCanvas* canvas, RenderNodeDrawable* caster) { const RenderProperties& casterProperties = caster->getNodeProperties(); if (casterProperties.getAlpha() <= 0.0f || casterProperties.getOutline().getAlpha() <= 0.0f || !casterProperties.getOutline().getPath() || casterProperties.getScaleX() == 0 || casterProperties.getScaleY() == 0) { // no shadow to draw return; } const SkScalar casterAlpha = casterProperties.getAlpha() * casterProperties.getOutline().getAlpha(); if (casterAlpha <= 0.0f) { return; } float ambientAlpha = (SkiaPipeline::getAmbientShadowAlpha() / 255.f) * casterAlpha; float spotAlpha = (SkiaPipeline::getSpotShadowAlpha() / 255.f) * casterAlpha; const RevealClip& revealClip = casterProperties.getRevealClip(); const SkPath* revealClipPath = revealClip.getPath(); if (revealClipPath && revealClipPath->isEmpty()) { // An empty reveal clip means nothing is drawn return; } bool clippedToBounds = casterProperties.getClippingFlags() & CLIP_TO_CLIP_BOUNDS; SkRect casterClipRect = SkRect::MakeEmpty(); if (clippedToBounds) { Rect clipBounds; casterProperties.getClippingRectForFlags(CLIP_TO_CLIP_BOUNDS, &clipBounds); casterClipRect = clipBounds.toSkRect(); if (casterClipRect.isEmpty()) { // An empty clip rect means nothing is drawn return; } } SkAutoCanvasRestore acr(canvas, true); SkMatrix shadowMatrix; mat4 hwuiMatrix; // TODO we don't pass the optional boolean to treat it as a 4x4 matrix caster->getRenderNode()->applyViewPropertyTransforms(hwuiMatrix); hwuiMatrix.copyTo(shadowMatrix); canvas->concat(shadowMatrix); // default the shadow-casting path to the outline of the caster const SkPath* casterPath = casterProperties.getOutline().getPath(); // intersect the shadow-casting path with the clipBounds, if present if (clippedToBounds && !casterClipRect.contains(casterPath->getBounds())) { casterPath = caster->getRenderNode()->getClippedOutline(casterClipRect); } // intersect the shadow-casting path with the reveal, if present SkPath tmpPath; // holds temporary SkPath to store the result of intersections if (revealClipPath) { Op(*casterPath, *revealClipPath, kIntersect_SkPathOp, &tmpPath); tmpPath.setIsVolatile(true); casterPath = &tmpPath; } const Vector3 lightPos = SkiaPipeline::getLightCenter(); SkPoint3 skiaLightPos = SkPoint3::Make(lightPos.x, lightPos.y, lightPos.z); SkPoint3 zParams; if (shadowMatrix.hasPerspective()) { // get the matrix with the full 3D transform mat4 zMatrix; caster->getRenderNode()->applyViewPropertyTransforms(zMatrix, true); zParams = SkPoint3::Make(zMatrix[2], zMatrix[6], zMatrix[mat4::kTranslateZ]); } else { zParams = SkPoint3::Make(0, 0, casterProperties.getZ()); } SkColor ambientColor = multiplyAlpha(casterProperties.getAmbientShadowColor(), ambientAlpha); SkColor spotColor = multiplyAlpha(casterProperties.getSpotShadowColor(), spotAlpha); SkShadowUtils::DrawShadow( canvas, *casterPath, zParams, skiaLightPos, SkiaPipeline::getLightRadius(), ambientColor, spotColor, casterAlpha < 1.0f ? SkShadowFlags::kTransparentOccluder_ShadowFlag : 0); }