Exemple #1
0
void draw(SkCanvas* canvas) {
    SkRect rect = SkRect::MakeLTRB(5, 35, 15, 25);
    SkDebugf("rect: %g, %g, %g, %g  isEmpty: %s\n", rect.left(), rect.top(), rect.right(),
              rect.bottom(), rect.isEmpty() ? "true" : "false");
    rect.sort();
    SkDebugf("rect: %g, %g, %g, %g  isEmpty: %s\n", rect.left(), rect.top(), rect.right(),
              rect.bottom(), rect.isEmpty() ? "true" : "false");
}
void CanvasContext::draw() {
    LOG_ALWAYS_FATAL_IF(!mCanvas || mEglSurface == EGL_NO_SURFACE,
            "drawRenderNode called on a context with no canvas or surface!");

    SkRect dirty;
    mDamageAccumulator.finish(&dirty);

    // TODO: Re-enable after figuring out cause of b/22592975
//    if (dirty.isEmpty() && Properties::skipEmptyFrames) {
//        mCurrentFrameInfo->addFlag(FrameInfoFlags::SkippedFrame);
//        return;
//    }

    mCurrentFrameInfo->markIssueDrawCommandsStart();

    EGLint width, height;
    mEglManager.beginFrame(mEglSurface, &width, &height);
    if (width != mCanvas->getViewportWidth() || height != mCanvas->getViewportHeight()) {
        mCanvas->setViewport(width, height);
        dirty.setEmpty();
    } else if (!mBufferPreserved || mHaveNewSurface) {
        dirty.setEmpty();
    } else {
        if (!dirty.isEmpty() && !dirty.intersect(0, 0, width, height)) {
            ALOGW("Dirty " RECT_STRING " doesn't intersect with 0 0 %d %d ?",
                    SK_RECT_ARGS(dirty), width, height);
            dirty.setEmpty();
        }
        profiler().unionDirty(&dirty);
    }

    if (!dirty.isEmpty()) {
        mCanvas->prepareDirty(dirty.fLeft, dirty.fTop,
                dirty.fRight, dirty.fBottom, mOpaque);
    } else {
        mCanvas->prepare(mOpaque);
    }

    Rect outBounds;
    mCanvas->drawRenderNode(mRootRenderNode.get(), outBounds);

    profiler().draw(mCanvas);

    bool drew = mCanvas->finish();

    // Even if we decided to cancel the frame, from the perspective of jank
    // metrics the frame was swapped at this point
    mCurrentFrameInfo->markSwapBuffers();

    if (drew) {
        swapBuffers(dirty, width, height);
    }

    // TODO: Use a fence for real completion?
    mCurrentFrameInfo->markFrameCompleted();
    mJankTracker.addFrame(*mCurrentFrameInfo);
    mRenderThread.jankTracker().addFrame(*mCurrentFrameInfo);
}
Exemple #3
0
sk_sp<SkPicture> SkPictureRecorder::finishRecordingAsPicture(uint32_t finishFlags) {
    fActivelyRecording = false;
    fRecorder->restoreToCount(1);  // If we were missing any restores, add them now.

    if (fRecord->count() == 0) {
        if (finishFlags & kReturnNullForEmpty_FinishFlag) {
            return nullptr;
        }
        return fMiniRecorder.detachAsPicture(fCullRect);
    }

    // TODO: delay as much of this work until just before first playback?
    SkRecordOptimize(fRecord);

    if (fRecord->count() == 0) {
        if (finishFlags & kReturnNullForEmpty_FinishFlag) {
            return nullptr;
        }
    }

    SkAutoTUnref<SkLayerInfo> saveLayerData;

    if (fBBH && (fFlags & kComputeSaveLayerInfo_RecordFlag)) {
        saveLayerData.reset(new SkLayerInfo);
    }

    SkDrawableList* drawableList = fRecorder->getDrawableList();
    SkBigPicture::SnapshotArray* pictList =
        drawableList ? drawableList->newDrawableSnapshot() : nullptr;

    if (fBBH.get()) {
        SkAutoTMalloc<SkRect> bounds(fRecord->count());
        if (saveLayerData) {
            SkRecordComputeLayers(fCullRect, *fRecord, bounds, pictList, saveLayerData);
        } else {
            SkRecordFillBounds(fCullRect, *fRecord, bounds);
        }
        fBBH->insert(bounds, fRecord->count());

        // Now that we've calculated content bounds, we can update fCullRect, often trimming it.
        // TODO: get updated fCullRect from bounds instead of forcing the BBH to return it?
        SkRect bbhBound = fBBH->getRootBound();
        SkASSERT((bbhBound.isEmpty() || fCullRect.contains(bbhBound))
            || (bbhBound.isEmpty() && fCullRect.isEmpty()));
        fCullRect = bbhBound;
    }

    size_t subPictureBytes = fRecorder->approxBytesUsedBySubPictures();
    for (int i = 0; pictList && i < pictList->count(); i++) {
        subPictureBytes += SkPictureUtils::ApproximateBytesUsed(pictList->begin()[i]);
    }
    return sk_make_sp<SkBigPicture>(fCullRect, fRecord.release(), pictList, fBBH.release(),
                            saveLayerData.release(), subPictureBytes);
}
void BitmapImage::draw(GraphicsContext* ctxt, const FloatRect& dstRect,
                       const FloatRect& srcRect, CompositeOperator compositeOp)
{
    startAnimation();

    SkBitmapRef* image = this->nativeImageForCurrentFrame();
    if (!image) { // If it's too early we won't have an image yet.
        return;
    }

    // in case we get called with an incomplete bitmap
    const SkBitmap& bitmap = image->bitmap();
    if (bitmap.getPixels() == NULL && bitmap.pixelRef() == NULL) {
#ifdef TRACE_SKIPPED_BITMAPS
        SkDebugf("----- skip bitmapimage: [%d %d] pixels %p pixelref %p\n",
                 bitmap.width(), bitmap.height(),
                 bitmap.getPixels(), bitmap.pixelRef());
#endif
        return;
    }

    SkIRect srcR;
    SkRect  dstR;    
    float invScaleX = (float)bitmap.width() / image->origWidth();
    float invScaleY = (float)bitmap.height() / image->origHeight();

    android_setrect(&dstR, dstRect);
    android_setrect_scaled(&srcR, srcRect, invScaleX, invScaleY);
    if (srcR.isEmpty() || dstR.isEmpty()) {
#ifdef TRACE_SKIPPED_BITMAPS
        SkDebugf("----- skip bitmapimage: [%d %d] src-empty %d dst-empty %d\n",
                 bitmap.width(), bitmap.height(),
                 srcR.isEmpty(), dstR.isEmpty());
#endif
        return;
    }

    SkCanvas*   canvas = ctxt->platformContext()->mCanvas;
    SkPaint     paint;

    paint.setFilterBitmap(true);
    paint.setPorterDuffXfermode(android_convert_compositeOp(compositeOp));
    canvas->drawBitmapRect(bitmap, &srcR, dstR, &paint);

#ifdef TRACE_SUBSAMPLED_BITMAPS
    if (bitmap.width() != image->origWidth() ||
        bitmap.height() != image->origHeight()) {
        SkDebugf("--- BitmapImage::draw [%d %d] orig [%d %d]\n",
                 bitmap.width(), bitmap.height(),
                 image->origWidth(), image->origHeight());
    }
#endif
}
void CanvasContext::draw() {
    LOG_ALWAYS_FATAL_IF(!mCanvas || mEglSurface == EGL_NO_SURFACE,
            "drawRenderNode called on a context with no canvas or surface!");

    profiler().markPlaybackStart();

    SkRect dirty;
    mDamageAccumulator.finish(&dirty);

    EGLint width, height;
    mEglManager.beginFrame(mEglSurface, &width, &height);
    if (width != mCanvas->getViewportWidth() || height != mCanvas->getViewportHeight()) {
        mCanvas->setViewport(width, height);
        dirty.setEmpty();
    } else if (!mBufferPreserved || mHaveNewSurface) {
        dirty.setEmpty();
    } else {
        if (!dirty.isEmpty() && !dirty.intersect(0, 0, width, height)) {
            //ALOGW("Dirty " RECT_STRING " doesn't intersect with 0 0 %d %d ?",
            //        SK_RECT_ARGS(dirty), width, height);
            dirty.setEmpty();
        }
        profiler().unionDirty(&dirty);
    }

    status_t status;
    if (!dirty.isEmpty()) {
        status = mCanvas->prepareDirty(dirty.fLeft, dirty.fTop,
                dirty.fRight, dirty.fBottom, mOpaque);
    } else {
        status = mCanvas->prepare(mOpaque);
    }

    Rect outBounds;
    status |= mCanvas->drawRenderNode(mRootRenderNode.get(), outBounds);

    profiler().draw(mCanvas);

    mCanvas->finish();

    profiler().markPlaybackEnd();

    if (status & DrawGlInfo::kStatusDrew) {
        swapBuffers();
    } else {
        mEglManager.cancelFrame();
    }

    profiler().finishFrame();
}
Exemple #6
0
bool SkRect::intersect(const SkRect& a, const SkRect& b) {
    SkASSERT(&a && &b);

    if (!a.isEmpty() && !b.isEmpty() &&
        a.fLeft < b.fRight && b.fLeft < a.fRight &&
        a.fTop < b.fBottom && b.fTop < a.fBottom) {
        fLeft   = SkMaxScalar(a.fLeft,   b.fLeft);
        fTop    = SkMaxScalar(a.fTop,    b.fTop);
        fRight  = SkMinScalar(a.fRight,  b.fRight);
        fBottom = SkMinScalar(a.fBottom, b.fBottom);
        return true;
    }
    return false;
}
Exemple #7
0
 void draw_fill(SkCanvas* canvas, const SkRect& rect, SkScalar width) {
     if (rect.isEmpty()) {
         return;
     }
     SkPaint paint;
     paint.setColor(0x1f1f0f0f);
     paint.setStyle(SkPaint::kStroke_Style);
     paint.setStrokeWidth(width);
     SkPath path;
     SkScalar maxSide = SkTMax(rect.width(), rect.height()) / 2;
     SkPoint center = { rect.fLeft + maxSide, rect.fTop + maxSide };
     path.addCircle(center.fX, center.fY, maxSide);
     canvas->drawPath(path, paint);
     paint.setStyle(SkPaint::kFill_Style);
     path.reset();
     path.addCircle(center.fX, center.fY, maxSide - width / 2);
     paint.setColor(0x3f0f1f3f);
     canvas->drawPath(path, paint);
     path.reset();
     path.setFillType(SkPath::kEvenOdd_FillType);
     path.addCircle(center.fX, center.fY, maxSide + width / 2);
     SkRect outside = SkRect::MakeXYWH(center.fX - maxSide - width, center.fY - maxSide - width, 
             (maxSide + width) * 2, (maxSide + width) * 2);
     path.addRect(outside);
     canvas->drawPath(path, paint);
 }
Exemple #8
0
void SkRRect::setRectXY(const SkRect& rect, SkScalar xRad, SkScalar yRad) {
    if (rect.isEmpty() || !rect.isFinite()) {
        this->setEmpty();
        return;
    }

    if (!SkScalarsAreFinite(xRad, yRad)) {
        xRad = yRad = 0;    // devolve into a simple rect
    }
    if (xRad <= 0 || yRad <= 0) {
        // all corners are square in this case
        this->setRect(rect);
        return;
    }

    if (rect.width() < xRad+xRad || rect.height() < yRad+yRad) {
        SkScalar scale = SkMinScalar(rect.width() / (xRad + xRad), rect.height() / (yRad + yRad));
        SkASSERT(scale < SK_Scalar1);
        xRad = SkScalarMul(xRad, scale);
        yRad = SkScalarMul(yRad, scale);
    }

    fRect = rect;
    for (int i = 0; i < 4; ++i) {
        fRadii[i].set(xRad, yRad);
    }
    fType = kSimple_Type;
    if (xRad >= SkScalarHalf(fRect.width()) && yRad >= SkScalarHalf(fRect.height())) {
        fType = kOval_Type;
        // TODO: assert that all the x&y radii are already W/2 & H/2
    }

    SkDEBUGCODE(this->validate();)
}
Exemple #9
0
SkClipStack::SkClipStack(const SkRect& r)
    : fDeque(sizeof(Element), kDefaultElementAllocCnt)
    , fSaveCount(0) {
    if (!r.isEmpty()) {
        this->clipDevRect(r, SkRegion::kReplace_Op, false);
    }
}
Exemple #10
0
bool SkBBoxRecord::transformBounds(const SkRect& bounds, const SkPaint* paint) {
    SkRect outBounds = bounds;
    outBounds.sort();

    if (paint) {
        // account for stroking, path effects, shadows, etc
        if (paint->canComputeFastBounds()) {
            SkRect temp;
            outBounds = paint->computeFastBounds(outBounds, &temp);
        } else {
            // set bounds to current clip
            if (!this->getClipBounds(&outBounds)) {
                // current clip is empty
                return false;
            }
        }
    }

    if (!outBounds.isEmpty() && !this->quickReject(outBounds)) {
        this->getTotalMatrix().mapRect(&outBounds);
        this->handleBBox(outBounds);
        return true;
    }

    return false;
}
Exemple #11
0
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;
}
void OpaqueRegionSkia::applyOpaqueRegionFromLayer(const GraphicsContext* context, const SkRect& layerOpaqueRect, const SkPaint& paint)
{
    SkRect deviceClipRect;
    bool deviceClipIsARect = getDeviceClipAsRect(context, deviceClipRect);

    if (deviceClipRect.isEmpty())
        return;

    SkRect sourceOpaqueRect = layerOpaqueRect;
    // Save the opaque area in the destination, so we can preserve the parts of it under the source opaque area if possible.
    SkRect destinationOpaqueRect = currentTrackingOpaqueRect();

    bool outsideSourceOpaqueRectPreservesOpaque = xfermodePreservesOpaque(paint, false);
    if (!outsideSourceOpaqueRectPreservesOpaque)
        markRectAsNonOpaque(deviceClipRect);

    if (!deviceClipIsARect)
        return;
    if (!sourceOpaqueRect.intersect(deviceClipRect))
        return;

    bool sourceOpaqueRectDrawsOpaque = paintIsOpaque(paint, FillOnly, 0);
    bool sourceOpaqueRectXfersOpaque = xfermodeIsOpaque(paint, sourceOpaqueRectDrawsOpaque);
    bool sourceOpaqueRectPreservesOpaque = xfermodePreservesOpaque(paint, sourceOpaqueRectDrawsOpaque);

    // If the layer's opaque area is being drawn opaque in the layer below, then mark it opaque. Otherwise,
    // if it preserves opaque then keep the intersection of the two.
    if (sourceOpaqueRectXfersOpaque)
        markRectAsOpaque(sourceOpaqueRect);
    else if (sourceOpaqueRectPreservesOpaque && sourceOpaqueRect.intersect(destinationOpaqueRect))
        markRectAsOpaque(sourceOpaqueRect);
}
Exemple #13
0
void SkTileGrid::insert(void* data, const SkRect& fbounds, bool) {
    SkASSERT(!fbounds.isEmpty());
    SkIRect dilatedBounds;
    if (fbounds.isLargest()) {
        // Dilating the largest SkIRect will overflow.  Other nearly-largest rects may overflow too,
        // but we don't make active use of them like we do the largest.
        dilatedBounds.setLargest();
    } else {
        fbounds.roundOut(&dilatedBounds);
        dilatedBounds.outset(fInfo.fMargin.width(), fInfo.fMargin.height());
        dilatedBounds.offset(fInfo.fOffset);
    }

    const SkIRect gridBounds =
        { 0, 0, fInfo.fTileInterval.width() * fXTiles, fInfo.fTileInterval.height() * fYTiles };
    if (!SkIRect::Intersects(dilatedBounds, gridBounds)) {
        return;
    }

    // Note: SkIRects are non-inclusive of the right() column and bottom() row,
    // hence the "-1"s in the computations of maxX and maxY.
    int minX = SkMax32(0, SkMin32(dilatedBounds.left() / fInfo.fTileInterval.width(), fXTiles - 1));
    int minY = SkMax32(0, SkMin32(dilatedBounds.top() / fInfo.fTileInterval.height(), fYTiles - 1));
    int maxX = SkMax32(0, SkMin32((dilatedBounds.right()  - 1) / fInfo.fTileInterval.width(),
                                  fXTiles - 1));
    int maxY = SkMax32(0, SkMin32((dilatedBounds.bottom() - 1) / fInfo.fTileInterval.height(),
                                  fYTiles - 1));

    Entry entry = { fCount++, data };
    for (int y = minY; y <= maxY; y++) {
        for (int x = minX; x <= maxX; x++) {
            fTiles[y * fXTiles + x].push(entry);
        }
    }
}
bool EglManager::swapBuffers(const Frame& frame, const SkRect& screenDirty) {
    if (CC_UNLIKELY(Properties::waitForGpuCompletion)) {
        ATRACE_NAME("Finishing GPU work");
        fence();
    }

    EGLint rects[4];
    frame.map(screenDirty, rects);
    eglSwapBuffersWithDamageKHR(mEglDisplay, frame.mSurface, rects, screenDirty.isEmpty() ? 0 : 1);

    EGLint err = eglGetError();
    if (CC_LIKELY(err == EGL_SUCCESS)) {
        return true;
    }
    if (err == EGL_BAD_SURFACE || err == EGL_BAD_NATIVE_WINDOW) {
        // For some reason our surface was destroyed out from under us
        // This really shouldn't happen, but if it does we can recover easily
        // by just not trying to use the surface anymore
        ALOGW("swapBuffers encountered EGL error %d on %p, halting rendering...", err,
              frame.mSurface);
        return false;
    }
    LOG_ALWAYS_FATAL("Encountered EGL error %d %s during rendering", err, egl_error_str(err));
    // Impossible to hit this, but the compiler doesn't know that
    return false;
}
Exemple #15
0
void SkRRect::setNinePatch(const SkRect& rect, SkScalar leftRad, SkScalar topRad,
                           SkScalar rightRad, SkScalar bottomRad) {
    if (rect.isEmpty() || !rect.isFinite()) {
        this->setEmpty();
        return;
    }

    const SkScalar array[4] = { leftRad, topRad, rightRad, bottomRad };
    if (!SkScalarsAreFinite(array, 4)) {
        this->setRect(rect);    // devolve into a simple rect
        return;
    }

    leftRad = SkMaxScalar(leftRad, 0);
    topRad = SkMaxScalar(topRad, 0);
    rightRad = SkMaxScalar(rightRad, 0);
    bottomRad = SkMaxScalar(bottomRad, 0);

    SkScalar scale = SK_Scalar1;
    if (leftRad + rightRad > rect.width()) {
        scale = rect.width() / (leftRad + rightRad);
    }
    if (topRad + bottomRad > rect.height()) {
        scale = SkMinScalar(scale, rect.height() / (topRad + bottomRad));
    }

    if (scale < SK_Scalar1) {
        leftRad = SkScalarMul(leftRad, scale);
        topRad = SkScalarMul(topRad, scale);
        rightRad = SkScalarMul(rightRad, scale);
        bottomRad = SkScalarMul(bottomRad, scale);
    }

    if (leftRad == rightRad && topRad == bottomRad) {
        if (leftRad >= SkScalarHalf(rect.width()) && topRad >= SkScalarHalf(rect.height())) {
            fType = kOval_Type;
        } else if (0 == leftRad || 0 == topRad) {
            // If the left and (by equality check above) right radii are zero then it is a rect.
            // Same goes for top/bottom.
            fType = kRect_Type;
            leftRad = 0;
            topRad = 0;
            rightRad = 0;
            bottomRad = 0;
        } else {
            fType = kSimple_Type;
        }
    } else {
        fType = kNinePatch_Type;
    }

    fRect = rect;
    fRadii[kUpperLeft_Corner].set(leftRad, topRad);
    fRadii[kUpperRight_Corner].set(rightRad, topRad);
    fRadii[kLowerRight_Corner].set(rightRad, bottomRad);
    fRadii[kLowerLeft_Corner].set(leftRad, bottomRad);

    SkDEBUGCODE(this->validate();)
}
Exemple #16
0
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;
}
Exemple #17
0
SkPicture* SkPictureRecorder::endRecordingAsPicture() {
    fActivelyRecording = false;
    fRecorder->restoreToCount(1);  // If we were missing any restores, add them now.

    if (fRecord->count() == 0) {
        return fMiniRecorder.detachAsPicture(fCullRect);
    }

    // TODO: delay as much of this work until just before first playback?
    SkRecordOptimize(fRecord);

    SkAutoTUnref<SkLayerInfo> saveLayerData;

    if (fBBH && (fFlags & kComputeSaveLayerInfo_RecordFlag)) {
        saveLayerData.reset(SkNEW(SkLayerInfo));
    }

    SkDrawableList* drawableList = fRecorder->getDrawableList();
    SkBigPicture::SnapshotArray* pictList =
        drawableList ? drawableList->newDrawableSnapshot() : NULL;

    if (fBBH.get()) {
        if (saveLayerData) {
            SkRecordComputeLayers(fCullRect, *fRecord, pictList, fBBH.get(), saveLayerData);
        } else {
            SkRecordFillBounds(fCullRect, *fRecord, fBBH.get());
        }
        SkRect bbhBound = fBBH->getRootBound();
        SkASSERT((bbhBound.isEmpty() || fCullRect.contains(bbhBound))
            || (bbhBound.isEmpty() && fCullRect.isEmpty()));
        fCullRect = bbhBound;
    }

    size_t subPictureBytes = fRecorder->approxBytesUsedBySubPictures();
    for (int i = 0; pictList && i < pictList->count(); i++) {
        subPictureBytes += SkPictureUtils::ApproximateBytesUsed(pictList->begin()[i]);
    }
    return SkNEW_ARGS(SkBigPicture, (fCullRect,
                                     fRecord.detach(),
                                     pictList,
                                     fBBH.detach(),
                                     saveLayerData.detach(),
                                     subPictureBytes));
}
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);
}
void Image::drawPattern(GraphicsContext* ctxt, const FloatRect& tileRect,
                        const TransformationMatrix& patternTransform,
                        const FloatPoint& phase, CompositeOperator compositeOp,
                        const FloatRect& destRect)
{
    SkBitmapRef* image = this->nativeImageForCurrentFrame();
    if (!image) { // If it's too early we won't have an image yet.
        return;
    }
    
    // in case we get called with an incomplete bitmap
    const SkBitmap& bitmap = image->bitmap();
    if (bitmap.getPixels() == NULL && bitmap.pixelRef() == NULL) {
        return;
    }
    
    SkRect  dstR;    
    android_setrect(&dstR, destRect);
    if (dstR.isEmpty()) {
        return;
    }
    
    SkCanvas*   canvas = ctxt->platformContext()->mCanvas;
    SkPaint     paint;
    
    SkShader* shader = SkShader::CreateBitmapShader(bitmap,
                                                    SkShader::kRepeat_TileMode,
                                                    SkShader::kRepeat_TileMode);
    paint.setShader(shader)->unref();
    // now paint is the only owner of shader
    paint.setPorterDuffXfermode(android_convert_compositeOp(compositeOp));
    paint.setFilterBitmap(true);
    
    SkMatrix matrix(patternTransform);
    
    float scaleX = (float)image->origWidth() / bitmap.width();
    float scaleY = (float)image->origHeight() / bitmap.height();
    matrix.preScale(SkFloatToScalar(scaleX), SkFloatToScalar(scaleY));
    
    matrix.postTranslate(SkFloatToScalar(phase.x()),
                         SkFloatToScalar(phase.y()));
    shader->setLocalMatrix(matrix);
    canvas->drawRect(dstR, paint);
    
#ifdef TRACE_SUBSAMPLED_BITMAPS
    if (bitmap.width() != image->origWidth() ||
        bitmap.height() != image->origHeight()) {
        SkDebugf("--- Image::drawPattern [%d %d] orig [%d %d] dst [%g %g]\n",
                 bitmap.width(), bitmap.height(),
                 image->origWidth(), image->origHeight(),
                 SkScalarToFloat(dstR.width()), SkScalarToFloat(dstR.height()));
    }
#endif
}
Exemple #20
0
static SkRect random_rect(SkRandom& rand) {
    SkRect rect = {0,0,0,0};
    while (rect.isEmpty()) {
        rect.fLeft   = rand.nextRangeF(0, MAX_SIZE);
        rect.fRight  = rand.nextRangeF(0, MAX_SIZE);
        rect.fTop    = rand.nextRangeF(0, MAX_SIZE);
        rect.fBottom = rand.nextRangeF(0, MAX_SIZE);
        rect.sort();
    }
    return rect;
}
Exemple #21
0
// found and fixed for android: not initializing rect for string's of length 0
static void regression_measureText(skiatest::Reporter* reporter) {

    SkPaint paint;
    paint.setTextSize(SkFloatToScalar(12.0f));

    SkRect r;
    r.setLTRB(SK_ScalarNaN, SK_ScalarNaN, SK_ScalarNaN, SK_ScalarNaN);

    // test that the rect was reset
    paint.measureText("", 0, &r, SkFloatToScalar(1.0f));
    REPORTER_ASSERT(reporter, r.isEmpty());
}
void LayerAndroid::clipInner(SkTDArray<SkRect>* region,
                             const SkRect& local) const
{
    SkRect localBounds;
    bounds(&localBounds);
    localBounds.intersect(local);
    if (localBounds.isEmpty())
        return;
    if (m_recordingPicture && boundsIsUnique(*region, localBounds))
        *region->append() = localBounds;
    for (int i = 0; i < countChildren(); i++)
        getChild(i)->clipInner(region, m_haveClip ? localBounds : local);
}
Exemple #23
0
void SkScan::FillRect(const SkRect& r, const SkRasterClip& clip,
                      SkBlitter* blitter) {
    if (clip.isEmpty() || r.isEmpty()) {
        return;
    }

    if (clip.isBW()) {
        FillRect(r, &clip.bwRgn(), blitter);
        return;
    }

    SkAAClipBlitterWrapper wrapper(clip, blitter);
    FillRect(r, &wrapper.getRgn(), wrapper.getBlitter());
}
void ShaderProgram::drawQuadInternal(SkRect& geometry,
                                     GLint textureId,
                                     float opacity,
                                     GLint program,
                                     GLint projectionMatrixHandle,
                                     GLint texSampler,
                                     GLenum textureTarget,
                                     GLint position,
                                     GLint alpha,
                                     GLint texFilter,
                                     GLint contrast)
{
    glUseProgram(program);

    if (!geometry.isEmpty())
         setProjectionMatrix(geometry, projectionMatrixHandle);
    else {
        TransformationMatrix matrix;
        // Map x,y from (0,1) to (-1, 1)
        matrix.scale3d(2, 2, 1);
        matrix.translate3d(-0.5, -0.5, 0);
        GLfloat projectionMatrix[16];
        GLUtils::toGLMatrix(projectionMatrix, matrix);
        glUniformMatrix4fv(projectionMatrixHandle, 1, GL_FALSE, projectionMatrix);
    }

    glActiveTexture(GL_TEXTURE0);
    glUniform1i(texSampler, 0);
    glBindTexture(textureTarget, textureId);
    glTexParameteri(textureTarget, GL_TEXTURE_MIN_FILTER, texFilter);
    glTexParameteri(textureTarget, GL_TEXTURE_MAG_FILTER, texFilter);
    glTexParameteri(textureTarget, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameteri(textureTarget, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);

    glBindBuffer(GL_ARRAY_BUFFER, m_textureBuffer[0]);
    glEnableVertexAttribArray(position);
    glVertexAttribPointer(position, 2, GL_FLOAT, GL_FALSE, 0, 0);
    glUniform1f(alpha, opacity);
    if (contrast != -1)
        glUniform1f(contrast, m_contrast);

    setBlendingState(opacity < 1.0);
    glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
}
static void test_isRectFinite(skiatest::Reporter* reporter) {
#ifdef SK_SCALAR_IS_FLOAT
    static const SkPoint gF0[] = {
        { 0, 0 }, { 1, 1 }
    };
    static const SkPoint gF1[] = {
        { 0, 0 }, { 1, 1 }, { 99.234f, -42342 }
    };

    static const SkPoint gI0[] = {
        { 0, 0 }, { 1, 1 }, { 99.234f, -42342 }, { SK_ScalarNaN, 3 }, { 2, 3 },
    };
    static const SkPoint gI1[] = {
        { 0, 0 }, { 1, 1 }, { 99.234f, -42342 }, { 3, SK_ScalarNaN }, { 2, 3 },
    };
    static const SkPoint gI2[] = {
        { 0, 0 }, { 1, 1 }, { 99.234f, -42342 }, { SK_ScalarInfinity, 3 }, { 2, 3 },
    };
    static const SkPoint gI3[] = {
        { 0, 0 }, { 1, 1 }, { 99.234f, -42342 }, { 3, SK_ScalarInfinity }, { 2, 3 },
    };

    static const struct {
        const SkPoint* fPts;
        size_t         fCount;
        bool           fIsFinite;
    } gSets[] = {
        { gF0, SK_ARRAY_COUNT(gF0), true },
        { gF1, SK_ARRAY_COUNT(gF1), true },

        { gI0, SK_ARRAY_COUNT(gI0), false },
        { gI1, SK_ARRAY_COUNT(gI1), false },
        { gI2, SK_ARRAY_COUNT(gI2), false },
        { gI3, SK_ARRAY_COUNT(gI3), false },
    };

    for (size_t i = 0; i < SK_ARRAY_COUNT(gSets); ++i) {
        SkRect r;
        r.set(gSets[i].fPts, gSets[i].fCount);
        bool rectIsFinite = !r.isEmpty();
        REPORTER_ASSERT(reporter, gSets[i].fIsFinite == rectIsFinite);
    }
#endif
}
Exemple #26
0
DEF_TEST(CanvasState_test_saveLayer_clip, reporter) {
    const int WIDTH = 100;
    const int HEIGHT = 100;
    const int LAYER_WIDTH = 50;
    const int LAYER_HEIGHT = 50;

    SkBitmap bitmap;
    bitmap.allocN32Pixels(WIDTH, HEIGHT);
    SkCanvas canvas(bitmap);

    SkRect bounds = SkRect::MakeWH(SkIntToScalar(LAYER_WIDTH), SkIntToScalar(LAYER_HEIGHT));
    canvas.clipRect(SkRect::MakeWH(SkIntToScalar(WIDTH), SkIntToScalar(HEIGHT)));

    // Check that saveLayer without the kClipToLayer_SaveFlag leaves the
    // clip stack unchanged.
    canvas.saveLayer(SkCanvas::SaveLayerRec(&bounds,
                                            nullptr,
                                            SkCanvas::kDontClipToLayer_Legacy_SaveLayerFlag));
    SkRect clipStackBounds;
    SkClipStack::BoundsType boundsType;
    canvas.getClipStack()->getBounds(&clipStackBounds, &boundsType);
    // The clip stack will return its bounds, or it may be "full" : i.e. empty + inside_out.
    // Either result is consistent with this test, since the canvas' size is WIDTH/HEIGHT
    if (SkClipStack::kInsideOut_BoundsType == boundsType) {
        REPORTER_ASSERT(reporter, clipStackBounds.isEmpty());
    } else {
        REPORTER_ASSERT(reporter, clipStackBounds.width() == WIDTH);
        REPORTER_ASSERT(reporter, clipStackBounds.height() == HEIGHT);
    }
    canvas.restore();

    // Check that saveLayer with the kClipToLayer_SaveFlag sets the clip
    // stack to the layer bounds.
    canvas.saveLayer(&bounds, nullptr);
    canvas.getClipStack()->getBounds(&clipStackBounds, &boundsType);
    REPORTER_ASSERT(reporter, clipStackBounds.width() == LAYER_WIDTH);
    REPORTER_ASSERT(reporter, clipStackBounds.height() == LAYER_HEIGHT);

    canvas.restore();
}
Exemple #27
0
void SkDebugCanvas::drawTo(SkCanvas* canvas, int index) {
    SkASSERT(!fCommandVector.isEmpty());
    SkASSERT(index < fCommandVector.count());

    int saveCount = canvas->save();

    SkRect windowRect = SkRect::MakeWH(SkIntToScalar(canvas->getBaseLayerSize().width()),
                                       SkIntToScalar(canvas->getBaseLayerSize().height()));

    bool pathOpsMode = getAllowSimplifyClip();
    canvas->setAllowSimplifyClip(pathOpsMode);
    canvas->clear(SK_ColorTRANSPARENT);
    canvas->resetMatrix();
    if (!windowRect.isEmpty()) {
        canvas->clipRect(windowRect, SkRegion::kReplace_Op);
    }
    this->applyUserTransform(canvas);

    if (fPaintFilterCanvas) {
        fPaintFilterCanvas->addCanvas(canvas);
        canvas = fPaintFilterCanvas.get();
    }

    if (fMegaVizMode) {
        this->markActiveCommands(index);
    }

    for (int i = 0; i <= index; i++) {
        if (i == index && fFilter) {
            canvas->clear(0xAAFFFFFF);
        }

        if (fCommandVector[i]->isVisible()) {
            if (fMegaVizMode && fCommandVector[i]->active()) {
                // "active" commands execute their visualization behaviors:
                //     All active saveLayers get replaced with saves so all draws go to the
                //     visible canvas.
                //     All active culls draw their cull box
                fCommandVector[i]->vizExecute(canvas);
            } else {
                fCommandVector[i]->setUserMatrix(fUserMatrix);
                fCommandVector[i]->execute(canvas);
            }
        }
    }

    if (fMegaVizMode) {
        canvas->save();
        // nuke the CTM
        canvas->resetMatrix();
        // turn off clipping
        if (!windowRect.isEmpty()) {
            SkRect r = windowRect;
            r.outset(SK_Scalar1, SK_Scalar1);
            canvas->clipRect(r, SkRegion::kReplace_Op);
        }
        // visualize existing clips
        SkDebugClipVisitor visitor(canvas);

        canvas->replayClips(&visitor);

        canvas->restore();
    }
    if (pathOpsMode) {
        this->resetClipStackData();
        const SkClipStack* clipStack = canvas->getClipStack();
        SkClipStack::Iter iter(*clipStack, SkClipStack::Iter::kBottom_IterStart);
        const SkClipStack::Element* element;
        SkPath devPath;
        while ((element = iter.next())) {
            SkClipStack::Element::Type type = element->getType();
            SkPath operand;
            if (type != SkClipStack::Element::kEmpty_Type) {
               element->asPath(&operand);
            }
            SkRegion::Op elementOp = element->getOp();
            this->addClipStackData(devPath, operand, elementOp);
            if (elementOp == SkRegion::kReplace_Op) {
                devPath = operand;
            } else {
                Op(devPath, operand, (SkPathOp) elementOp, &devPath);
            }
        }
        this->lastClipStackData(devPath);
    }
    fMatrix = canvas->getTotalMatrix();
    if (!canvas->getClipDeviceBounds(&fClip)) {
        fClip.setEmpty();
    }

    canvas->restoreToCount(saveCount);

    if (fPaintFilterCanvas) {
        fPaintFilterCanvas->removeAll();
    }
}
SkPDFImageShader::SkPDFImageShader(SkPDFShader::State* state) : fState(state) {
    fState.get()->fImage.lockPixels();

    SkMatrix finalMatrix = fState.get()->fCanvasTransform;
    finalMatrix.preConcat(fState.get()->fShaderTransform);
    SkRect surfaceBBox;
    surfaceBBox.set(fState.get()->fBBox);
    transformBBox(finalMatrix, &surfaceBBox);

    SkMatrix unflip;
    unflip.setTranslate(0, SkScalarRound(surfaceBBox.height()));
    unflip.preScale(SK_Scalar1, -SK_Scalar1);
    SkISize size = SkISize::Make(SkScalarRound(surfaceBBox.width()),
                                 SkScalarRound(surfaceBBox.height()));
    SkPDFDevice pattern(size, size, unflip);
    SkCanvas canvas(&pattern);
    canvas.translate(-surfaceBBox.fLeft, -surfaceBBox.fTop);
    finalMatrix.preTranslate(surfaceBBox.fLeft, surfaceBBox.fTop);

    const SkBitmap* image = &fState.get()->fImage;
    int width = image->width();
    int height = image->height();
    SkShader::TileMode tileModes[2];
    tileModes[0] = fState.get()->fImageTileModes[0];
    tileModes[1] = fState.get()->fImageTileModes[1];

    canvas.drawBitmap(*image, 0, 0);
    SkRect patternBBox = SkRect::MakeXYWH(-surfaceBBox.fLeft, -surfaceBBox.fTop,
                                          width, height);

    // Tiling is implied.  First we handle mirroring.
    if (tileModes[0] == SkShader::kMirror_TileMode) {
        SkMatrix xMirror;
        xMirror.setScale(-1, 1);
        xMirror.postTranslate(2 * width, 0);
        canvas.drawBitmapMatrix(*image, xMirror);
        patternBBox.fRight += width;
    }
    if (tileModes[1] == SkShader::kMirror_TileMode) {
        SkMatrix yMirror;
        yMirror.setScale(SK_Scalar1, -SK_Scalar1);
        yMirror.postTranslate(0, 2 * height);
        canvas.drawBitmapMatrix(*image, yMirror);
        patternBBox.fBottom += height;
    }
    if (tileModes[0] == SkShader::kMirror_TileMode &&
            tileModes[1] == SkShader::kMirror_TileMode) {
        SkMatrix mirror;
        mirror.setScale(-1, -1);
        mirror.postTranslate(2 * width, 2 * height);
        canvas.drawBitmapMatrix(*image, mirror);
    }

    // Then handle Clamping, which requires expanding the pattern canvas to
    // cover the entire surfaceBBox.

    // If both x and y are in clamp mode, we start by filling in the corners.
    // (Which are just a rectangles of the corner colors.)
    if (tileModes[0] == SkShader::kClamp_TileMode &&
            tileModes[1] == SkShader::kClamp_TileMode) {
        SkPaint paint;
        SkRect rect;
        rect = SkRect::MakeLTRB(surfaceBBox.fLeft, surfaceBBox.fTop, 0, 0);
        if (!rect.isEmpty()) {
            paint.setColor(image->getColor(0, 0));
            canvas.drawRect(rect, paint);
        }

        rect = SkRect::MakeLTRB(width, surfaceBBox.fTop, surfaceBBox.fRight, 0);
        if (!rect.isEmpty()) {
            paint.setColor(image->getColor(width - 1, 0));
            canvas.drawRect(rect, paint);
        }

        rect = SkRect::MakeLTRB(width, height, surfaceBBox.fRight,
                                surfaceBBox.fBottom);
        if (!rect.isEmpty()) {
            paint.setColor(image->getColor(width - 1, height - 1));
            canvas.drawRect(rect, paint);
        }

        rect = SkRect::MakeLTRB(surfaceBBox.fLeft, height, 0,
                                surfaceBBox.fBottom);
        if (!rect.isEmpty()) {
            paint.setColor(image->getColor(0, height - 1));
            canvas.drawRect(rect, paint);
        }
    }

    // Then expand the left, right, top, then bottom.
    if (tileModes[0] == SkShader::kClamp_TileMode) {
        SkIRect subset = SkIRect::MakeXYWH(0, 0, 1, height);
        if (surfaceBBox.fLeft < 0) {
            SkBitmap left;
            SkAssertResult(image->extractSubset(&left, subset));

            SkMatrix leftMatrix;
            leftMatrix.setScale(-surfaceBBox.fLeft, 1);
            leftMatrix.postTranslate(surfaceBBox.fLeft, 0);
            canvas.drawBitmapMatrix(left, leftMatrix);

            if (tileModes[1] == SkShader::kMirror_TileMode) {
                leftMatrix.postScale(SK_Scalar1, -SK_Scalar1);
                leftMatrix.postTranslate(0, 2 * height);
                canvas.drawBitmapMatrix(left, leftMatrix);
            }
            patternBBox.fLeft = 0;
        }

        if (surfaceBBox.fRight > width) {
            SkBitmap right;
            subset.offset(width - 1, 0);
            SkAssertResult(image->extractSubset(&right, subset));

            SkMatrix rightMatrix;
            rightMatrix.setScale(surfaceBBox.fRight - width, 1);
            rightMatrix.postTranslate(width, 0);
            canvas.drawBitmapMatrix(right, rightMatrix);

            if (tileModes[1] == SkShader::kMirror_TileMode) {
                rightMatrix.postScale(SK_Scalar1, -SK_Scalar1);
                rightMatrix.postTranslate(0, 2 * height);
                canvas.drawBitmapMatrix(right, rightMatrix);
            }
            patternBBox.fRight = surfaceBBox.width();
        }
    }

    if (tileModes[1] == SkShader::kClamp_TileMode) {
        SkIRect subset = SkIRect::MakeXYWH(0, 0, width, 1);
        if (surfaceBBox.fTop < 0) {
            SkBitmap top;
            SkAssertResult(image->extractSubset(&top, subset));

            SkMatrix topMatrix;
            topMatrix.setScale(SK_Scalar1, -surfaceBBox.fTop);
            topMatrix.postTranslate(0, surfaceBBox.fTop);
            canvas.drawBitmapMatrix(top, topMatrix);

            if (tileModes[0] == SkShader::kMirror_TileMode) {
                topMatrix.postScale(-1, 1);
                topMatrix.postTranslate(2 * width, 0);
                canvas.drawBitmapMatrix(top, topMatrix);
            }
            patternBBox.fTop = 0;
        }

        if (surfaceBBox.fBottom > height) {
            SkBitmap bottom;
            subset.offset(0, height - 1);
            SkAssertResult(image->extractSubset(&bottom, subset));

            SkMatrix bottomMatrix;
            bottomMatrix.setScale(SK_Scalar1, surfaceBBox.fBottom - height);
            bottomMatrix.postTranslate(0, height);
            canvas.drawBitmapMatrix(bottom, bottomMatrix);

            if (tileModes[0] == SkShader::kMirror_TileMode) {
                bottomMatrix.postScale(-1, 1);
                bottomMatrix.postTranslate(2 * width, 0);
                canvas.drawBitmapMatrix(bottom, bottomMatrix);
            }
            patternBBox.fBottom = surfaceBBox.height();
        }
    }

    SkRefPtr<SkPDFArray> patternBBoxArray = new SkPDFArray;
    patternBBoxArray->unref();  // SkRefPtr and new both took a reference.
    patternBBoxArray->reserve(4);
    patternBBoxArray->appendScalar(patternBBox.fLeft);
    patternBBoxArray->appendScalar(patternBBox.fTop);
    patternBBoxArray->appendScalar(patternBBox.fRight);
    patternBBoxArray->appendScalar(patternBBox.fBottom);

    // Put the canvas into the pattern stream (fContent).
    SkRefPtr<SkStream> content = pattern.content();
    content->unref();  // SkRefPtr and content() both took a reference.
    pattern.getResources(&fResources);

    setData(content.get());
    insertName("Type", "Pattern");
    insertInt("PatternType", 1);
    insertInt("PaintType", 1);
    insertInt("TilingType", 1);
    insert("BBox", patternBBoxArray.get());
    insertScalar("XStep", patternBBox.width());
    insertScalar("YStep", patternBBox.height());
    insert("Resources", pattern.getResourceDict());
    insert("Matrix", SkPDFUtils::MatrixToArray(finalMatrix))->unref();

    fState.get()->fImage.unlockPixels();
}
Exemple #29
0
SkPDFImageShader* SkPDFImageShader::Create(
        SkPDFCanon* canon,
        SkScalar dpi,
        SkAutoTDelete<SkPDFShader::State>* autoState) {
    const SkPDFShader::State& state = **autoState;

    state.fImage.lockPixels();

    // The image shader pattern cell will be drawn into a separate device
    // in pattern cell space (no scaling on the bitmap, though there may be
    // translations so that all content is in the device, coordinates > 0).

    // Map clip bounds to shader space to ensure the device is large enough
    // to handle fake clamping.
    SkMatrix finalMatrix = state.fCanvasTransform;
    finalMatrix.preConcat(state.fShaderTransform);
    SkRect deviceBounds;
    deviceBounds.set(state.fBBox);
    if (!inverse_transform_bbox(finalMatrix, &deviceBounds)) {
        return NULL;
    }

    const SkBitmap* image = &state.fImage;
    SkRect bitmapBounds;
    image->getBounds(&bitmapBounds);

    // For tiling modes, the bounds should be extended to include the bitmap,
    // otherwise the bitmap gets clipped out and the shader is empty and awful.
    // For clamp modes, we're only interested in the clip region, whether
    // or not the main bitmap is in it.
    SkShader::TileMode tileModes[2];
    tileModes[0] = state.fImageTileModes[0];
    tileModes[1] = state.fImageTileModes[1];
    if (tileModes[0] != SkShader::kClamp_TileMode ||
            tileModes[1] != SkShader::kClamp_TileMode) {
        deviceBounds.join(bitmapBounds);
    }

    SkISize size = SkISize::Make(SkScalarRoundToInt(deviceBounds.width()),
                                 SkScalarRoundToInt(deviceBounds.height()));
    SkAutoTUnref<SkPDFDevice> patternDevice(
            SkPDFDevice::CreateUnflipped(size, dpi, canon));
    SkCanvas canvas(patternDevice.get());

    SkRect patternBBox;
    image->getBounds(&patternBBox);

    // Translate the canvas so that the bitmap origin is at (0, 0).
    canvas.translate(-deviceBounds.left(), -deviceBounds.top());
    patternBBox.offset(-deviceBounds.left(), -deviceBounds.top());
    // Undo the translation in the final matrix
    finalMatrix.preTranslate(deviceBounds.left(), deviceBounds.top());

    // If the bitmap is out of bounds (i.e. clamp mode where we only see the
    // stretched sides), canvas will clip this out and the extraneous data
    // won't be saved to the PDF.
    canvas.drawBitmap(*image, 0, 0);

    SkScalar width = SkIntToScalar(image->width());
    SkScalar height = SkIntToScalar(image->height());

    // Tiling is implied.  First we handle mirroring.
    if (tileModes[0] == SkShader::kMirror_TileMode) {
        SkMatrix xMirror;
        xMirror.setScale(-1, 1);
        xMirror.postTranslate(2 * width, 0);
        drawBitmapMatrix(&canvas, *image, xMirror);
        patternBBox.fRight += width;
    }
    if (tileModes[1] == SkShader::kMirror_TileMode) {
        SkMatrix yMirror;
        yMirror.setScale(SK_Scalar1, -SK_Scalar1);
        yMirror.postTranslate(0, 2 * height);
        drawBitmapMatrix(&canvas, *image, yMirror);
        patternBBox.fBottom += height;
    }
    if (tileModes[0] == SkShader::kMirror_TileMode &&
            tileModes[1] == SkShader::kMirror_TileMode) {
        SkMatrix mirror;
        mirror.setScale(-1, -1);
        mirror.postTranslate(2 * width, 2 * height);
        drawBitmapMatrix(&canvas, *image, mirror);
    }

    // Then handle Clamping, which requires expanding the pattern canvas to
    // cover the entire surfaceBBox.

    // If both x and y are in clamp mode, we start by filling in the corners.
    // (Which are just a rectangles of the corner colors.)
    if (tileModes[0] == SkShader::kClamp_TileMode &&
            tileModes[1] == SkShader::kClamp_TileMode) {
        SkPaint paint;
        SkRect rect;
        rect = SkRect::MakeLTRB(deviceBounds.left(), deviceBounds.top(), 0, 0);
        if (!rect.isEmpty()) {
            paint.setColor(image->getColor(0, 0));
            canvas.drawRect(rect, paint);
        }

        rect = SkRect::MakeLTRB(width, deviceBounds.top(),
                                deviceBounds.right(), 0);
        if (!rect.isEmpty()) {
            paint.setColor(image->getColor(image->width() - 1, 0));
            canvas.drawRect(rect, paint);
        }

        rect = SkRect::MakeLTRB(width, height,
                                deviceBounds.right(), deviceBounds.bottom());
        if (!rect.isEmpty()) {
            paint.setColor(image->getColor(image->width() - 1,
                                           image->height() - 1));
            canvas.drawRect(rect, paint);
        }

        rect = SkRect::MakeLTRB(deviceBounds.left(), height,
                                0, deviceBounds.bottom());
        if (!rect.isEmpty()) {
            paint.setColor(image->getColor(0, image->height() - 1));
            canvas.drawRect(rect, paint);
        }
    }

    // Then expand the left, right, top, then bottom.
    if (tileModes[0] == SkShader::kClamp_TileMode) {
        SkIRect subset = SkIRect::MakeXYWH(0, 0, 1, image->height());
        if (deviceBounds.left() < 0) {
            SkBitmap left;
            SkAssertResult(image->extractSubset(&left, subset));

            SkMatrix leftMatrix;
            leftMatrix.setScale(-deviceBounds.left(), 1);
            leftMatrix.postTranslate(deviceBounds.left(), 0);
            drawBitmapMatrix(&canvas, left, leftMatrix);

            if (tileModes[1] == SkShader::kMirror_TileMode) {
                leftMatrix.postScale(SK_Scalar1, -SK_Scalar1);
                leftMatrix.postTranslate(0, 2 * height);
                drawBitmapMatrix(&canvas, left, leftMatrix);
            }
            patternBBox.fLeft = 0;
        }

        if (deviceBounds.right() > width) {
            SkBitmap right;
            subset.offset(image->width() - 1, 0);
            SkAssertResult(image->extractSubset(&right, subset));

            SkMatrix rightMatrix;
            rightMatrix.setScale(deviceBounds.right() - width, 1);
            rightMatrix.postTranslate(width, 0);
            drawBitmapMatrix(&canvas, right, rightMatrix);

            if (tileModes[1] == SkShader::kMirror_TileMode) {
                rightMatrix.postScale(SK_Scalar1, -SK_Scalar1);
                rightMatrix.postTranslate(0, 2 * height);
                drawBitmapMatrix(&canvas, right, rightMatrix);
            }
            patternBBox.fRight = deviceBounds.width();
        }
    }

    if (tileModes[1] == SkShader::kClamp_TileMode) {
        SkIRect subset = SkIRect::MakeXYWH(0, 0, image->width(), 1);
        if (deviceBounds.top() < 0) {
            SkBitmap top;
            SkAssertResult(image->extractSubset(&top, subset));

            SkMatrix topMatrix;
            topMatrix.setScale(SK_Scalar1, -deviceBounds.top());
            topMatrix.postTranslate(0, deviceBounds.top());
            drawBitmapMatrix(&canvas, top, topMatrix);

            if (tileModes[0] == SkShader::kMirror_TileMode) {
                topMatrix.postScale(-1, 1);
                topMatrix.postTranslate(2 * width, 0);
                drawBitmapMatrix(&canvas, top, topMatrix);
            }
            patternBBox.fTop = 0;
        }

        if (deviceBounds.bottom() > height) {
            SkBitmap bottom;
            subset.offset(0, image->height() - 1);
            SkAssertResult(image->extractSubset(&bottom, subset));

            SkMatrix bottomMatrix;
            bottomMatrix.setScale(SK_Scalar1, deviceBounds.bottom() - height);
            bottomMatrix.postTranslate(0, height);
            drawBitmapMatrix(&canvas, bottom, bottomMatrix);

            if (tileModes[0] == SkShader::kMirror_TileMode) {
                bottomMatrix.postScale(-1, 1);
                bottomMatrix.postTranslate(2 * width, 0);
                drawBitmapMatrix(&canvas, bottom, bottomMatrix);
            }
            patternBBox.fBottom = deviceBounds.height();
        }
    }

    // Put the canvas into the pattern stream (fContent).
    SkAutoTDelete<SkStreamAsset> content(patternDevice->content());

    SkPDFImageShader* imageShader =
            SkNEW_ARGS(SkPDFImageShader, (autoState->detach()));
    imageShader->setData(content.get());

    SkAutoTUnref<SkPDFDict> resourceDict(
            patternDevice->createResourceDict());
    populate_tiling_pattern_dict(imageShader, patternBBox,
                                 resourceDict.get(), finalMatrix);

    imageShader->fShaderState->fImage.unlockPixels();

    canon->addImageShader(imageShader);
    return imageShader;
}
Exemple #30
0
static void paintSkBitmap(PlatformContextSkia* platformContext, const NativeImageSkia& bitmap, const SkRect& srcRect, const SkRect& destRect, const SkXfermode::Mode& compOp)
{
#if PLATFORM(CHROMIUM)
    TRACE_EVENT0("skia", "paintSkBitmap");
#endif
    SkPaint paint;
    paint.setXfermodeMode(compOp);
    paint.setAlpha(platformContext->getNormalizedAlpha());
    paint.setLooper(platformContext->getDrawLooper());
    // only antialias if we're rotated or skewed
    paint.setAntiAlias(hasNon90rotation(platformContext));

    SkCanvas* canvas = platformContext->canvas();

    ResamplingMode resampling;
    if (platformContext->isAccelerated())
        resampling = RESAMPLE_LINEAR;
    else if (platformContext->printing())
        resampling = RESAMPLE_NONE;
    else {
        // Take into account scale applied to the canvas when computing sampling mode (e.g. CSS scale or page scale).
        SkRect destRectTarget = destRect;
        if (!(canvas->getTotalMatrix().getType() & (SkMatrix::kAffine_Mask | SkMatrix::kPerspective_Mask)))
            canvas->getTotalMatrix().mapRect(&destRectTarget, destRect);

        resampling = computeResamplingMode(canvas->getTotalMatrix(), bitmap,
            SkScalarToFloat(srcRect.width()), SkScalarToFloat(srcRect.height()),
            SkScalarToFloat(destRectTarget.width()), SkScalarToFloat(destRectTarget.height()));
    }

    if (resampling == RESAMPLE_NONE) {
      // FIXME: This is to not break tests (it results in the filter bitmap flag
      // being set to true). We need to decide if we respect RESAMPLE_NONE
      // being returned from computeResamplingMode.
        resampling = RESAMPLE_LINEAR;
    }
    resampling = limitResamplingMode(platformContext, resampling);
    paint.setFilterBitmap(resampling == RESAMPLE_LINEAR);
    if (resampling == RESAMPLE_AWESOME)
        drawResampledBitmap(*canvas, paint, bitmap, srcRect, destRect);
    else {
        // No resampling necessary, we can just draw the bitmap. We want to
        // filter it if we decided to do linear interpolation above, or if there
        // is something interesting going on with the matrix (like a rotation).
        // Note: for serialization, we will want to subset the bitmap first so
        // we don't send extra pixels.
        SkIRect enclosingSrcRect;
        SkRect enclosingDestRect;
        SkISize bitmapSize = SkISize::Make(bitmap.bitmap().width(), bitmap.bitmap().height());
        bool needsClipping = computeBitmapDrawRects(bitmapSize, srcRect, destRect, &enclosingSrcRect, &enclosingDestRect);

        if (enclosingSrcRect.isEmpty() || enclosingDestRect.isEmpty())
            return;

        // If destination is enlarged because source rectangle didn't align to
        // integer boundaries then we draw a slightly larger rectangle and clip
        // to the original destination rectangle.
        // See http://crbug.com/145540.
        if (needsClipping) {
            platformContext->save();
            platformContext->canvas()->clipRect(destRect);
        }

        canvas->drawBitmapRect(bitmap.bitmap(), &enclosingSrcRect, enclosingDestRect, &paint);

        if (needsClipping)
            platformContext->restore();
    }
    platformContext->didDrawRect(destRect, paint, &bitmap.bitmap());
}