Esempio n. 1
0
void FindCanvas::findHelper(const void* text, size_t byteLength,
                          const SkPaint& paint, const SkScalar positions[], 
                          SkScalar y,
                          SkRect (FindCanvas::*addMatch)(int index,
                          const SkPaint& paint, int count,
                          const uint16_t* glyphs,
                          const SkScalar positions[], SkScalar y)) {
    //SkASSERT(paint.getTextEncoding() == SkPaint::kGlyphID_TextEncoding);
    SkASSERT(mMatches);
    GlyphSet* glyphSet = getGlyphs(paint);
    const int count = glyphSet->getCount();
    int numCharacters = byteLength >> 1;
    const uint16_t* chars = (const uint16_t*) text;
    // This block will check to see if we are continuing from another line.  If
    // so, the user needs to have added a space, which we do not draw.
    if (mWorkingIndex) {
        SkPoint newY;
        getTotalMatrix().mapXY(0, y, &newY);
        SkIRect workingBounds = mWorkingRegion.getBounds();
        int newYInt = SkScalarRound(newY.fY);
        if (workingBounds.fTop > newYInt) {
            // The new text is above the working region, so we know it's not
            // a continuation.
            resetWorkingCanvas();
            mWorkingIndex = 0;
            mWorkingRegion.setEmpty();
        } else if (workingBounds.fBottom < newYInt) {
            // Now we know that this line is lower than our partial match.
            SkPaint clonePaint(paint);
            clonePaint.setTextEncoding(SkPaint::kUTF8_TextEncoding);
            uint16_t space;
            clonePaint.textToGlyphs(" ", 1, &space);
            if (glyphSet->characterMatches(space, mWorkingIndex)) {
                mWorkingIndex++;
                if (mWorkingIndex == count) {
                    // We already know that it is not clipped out because we
                    // checked for that before saving the working region.
                    insertMatchInfo(mWorkingRegion);

                    resetWorkingCanvas();
                    mWorkingIndex = 0;
                    mWorkingRegion.setEmpty();
                    // We have found a match, so continue on this line from
                    // scratch.
                }
            } else {
                resetWorkingCanvas();
                mWorkingIndex = 0;
                mWorkingRegion.setEmpty();
            }
        }
        // If neither one is true, then we are likely continuing on the same
        // line, but are in a new draw call because the paint has changed.  In
        // this case, we can continue without adding a space.
    }
    // j is the position in the search text
    // Start off with mWorkingIndex in case we are continuing from a prior call
    int j = mWorkingIndex;
    // index is the position in the drawn text
    int index = 0;
    for ( ; index != numCharacters; index++) {
        if (glyphSet->characterMatches(chars[index], j)) {
            // The jth character in the search text matches the indexth position
            // in the drawn text, so increase j.
            j++;
            if (j != count) {
                continue;
            }
            // The last count characters match, so we found the entire
            // search string.
            int remaining = count - mWorkingIndex;
            int matchIndex = index - remaining + 1;
            // Set up a pointer to the matching text in 'chars'.
            const uint16_t* glyphs = chars + matchIndex;
            SkRect rect = (this->*addMatch)(matchIndex, paint,
                    remaining, glyphs, positions, y);
            // We need an SkIRect for SkRegion operations.
            SkIRect iRect;
            rect.roundOut(&iRect);
            // If the rectangle is partially clipped, assume that the text is
            // not visible, so skip this match.
            if (getTotalClip().contains(iRect)) {
                // Want to outset the drawn rectangle by the same amount as
                // mOutset
                iRect.inset(-INTEGER_OUTSET, -INTEGER_OUTSET);
                SkRegion regionToAdd(iRect);
                if (!mWorkingRegion.isEmpty()) {
                    // If this is on the same line as our working region, make
                    // sure that they are close enough together that they are
                    // supposed to be part of the same text string.
                    // The width of two spaces has arbitrarily been chosen.
                    const SkIRect& workingBounds = mWorkingRegion.getBounds();
                    if (workingBounds.fTop <= iRect.fBottom &&
                            workingBounds.fBottom >= iRect.fTop &&
                            SkIntToScalar(iRect.fLeft - workingBounds.fRight) > 
                            approximateSpaceWidth(paint)) {
                        index = -1;     // Will increase to 0 on next run
                        // In this case, we need to start from the beginning of
                        // the text being searched and our search term.
                        j = 0;
                        mWorkingIndex = 0;
                        mWorkingRegion.setEmpty();
                        continue;
                    }
                    // Add the mWorkingRegion, which contains rectangles from
                    // the previous line(s).
                    regionToAdd.op(mWorkingRegion, SkRegion::kUnion_Op);
                }
                insertMatchInfo(regionToAdd);
#if INCLUDE_SUBSTRING_MATCHES
                // Reset index to the location of the match and reset j to the
                // beginning, so that on the next iteration of the loop, index
                // will advance by 1 and we will compare the next character in
                // chars to the first character in the GlyphSet.
                index = matchIndex;
#endif
            } else {
                // This match was clipped out, so begin looking at the next
                // character from our hidden match
                index = matchIndex;
            }
            // Whether the clip contained it or not, we need to start over
            // with our recording canvas
            resetWorkingCanvas();
        } else {
            // Index needs to be set to index - j + 1.
            // This is a ridiculous case, but imagine the situation where the
            // user is looking for the string "jjog" in the drawText call for
            // "jjjog".  The first two letters match.  However, when the index
            // is 2, and we discover that 'o' and 'j' do not match, we should go
            // back to 1, where we do, in fact, have a match
            // FIXME: This does not work if (as in our example) "jj" is in one
            // draw call and "jog" is in the next.  Doing so would require a
            // stack, keeping track of multiple possible working indeces and
            // regions.  This is likely an uncommon case.
            index = index - j;  // index will be increased by one on the next
                                // iteration
        }
        // We reach here in one of two cases:
        // 1) We just completed a match, so any working rectangle/index is no
        // longer needed, and we will start over from the beginning
        // 2) The glyphs do not match, so we start over at the beginning of
        // the search string.
        j = 0;
        mWorkingIndex = 0;
        mWorkingRegion.setEmpty();
    }
    // At this point, we have searched all of the text in the current drawText
    // call.
    // Keep track of a partial match that may start on this line.
    if (j > 0) {    // if j is greater than 0, we have a partial match
        int relativeCount = j - mWorkingIndex;  // Number of characters in this
                                                // part of the match.
        int partialIndex = index - relativeCount; // Index that starts our
                                                  // partial match.
        const uint16_t* partialGlyphs = chars + partialIndex;
        SkRect partial = (this->*addMatch)(partialIndex, paint, relativeCount,
                partialGlyphs, positions, y);
        partial.inset(mOutset, mOutset);
        SkIRect dest;
        partial.roundOut(&dest);
        // Only save a partial if it is in the current clip.
        if (getTotalClip().contains(dest)) {
            mWorkingRegion.op(dest, SkRegion::kUnion_Op);
            mWorkingIndex = j;
            return;
        }
    }
    // This string doesn't go into the next drawText, so reset our working
    // variables
    mWorkingRegion.setEmpty();
    mWorkingIndex = 0;
}
Esempio n. 2
0
static SkRect offset_center_to(const SkIRect& src, SkScalar x, SkScalar y) {
    SkScalar halfW = 0.5f * src.width();
    SkScalar halfH = 0.5f * src.height();

    return SkRect::MakeLTRB(x - halfW, y - halfH, x + halfW, y + halfH);
}
Esempio n. 3
0
// Atlased layers must be small enough to fit in the atlas, not have a
// paint with an image filter and be neither nested nor nesting.
// TODO: allow leaf nested layers to appear in the atlas.
void GrLayerHoister::FindLayersToAtlas(GrContext* context,
                                       const SkPicture* topLevelPicture,
                                       const SkMatrix& initialMat,
                                       const SkRect& query,
                                       SkTDArray<GrHoistedLayer>* atlased,
                                       SkTDArray<GrHoistedLayer>* recycled,
                                       int numSamples) {
    if (0 != numSamples) {
        // MSAA layers are currently never atlased
        return;
    }

    GrLayerCache* layerCache = context->getLayerCache();
    layerCache->processDeletedPictures();

    const SkBigPicture::AccelData* topLevelData = nullptr;
    if (const SkBigPicture* bp = topLevelPicture->asSkBigPicture()) {
        topLevelData = bp->accelData();
    }
    if (!topLevelData) {
        return;
    }

    const SkLayerInfo *topLevelGPUData = static_cast<const SkLayerInfo*>(topLevelData);
    if (0 == topLevelGPUData->numBlocks()) {
        return;
    }

    atlased->setReserve(atlased->count() + topLevelGPUData->numBlocks());

    for (int i = 0; i < topLevelGPUData->numBlocks(); ++i) {
        const SkLayerInfo::BlockInfo& info = topLevelGPUData->block(i);

        // TODO: ignore perspective projected layers here?
        bool disallowAtlasing = info.fHasNestedLayers || info.fIsNested ||
                                (info.fPaint && info.fPaint->getImageFilter());

        if (disallowAtlasing) {
            continue;
        }

        SkRect layerRect;
        initialMat.mapRect(&layerRect, info.fBounds);
        if (!layerRect.intersect(query)) {
            continue;
        }

        const SkIRect dstIR = layerRect.roundOut();

        SkIRect srcIR;

        if (!compute_source_rect(info, initialMat, dstIR, &srcIR) ||
            !GrLayerCache::PlausiblyAtlasable(srcIR.width(), srcIR.height())) {
            continue;
        }

        prepare_for_hoisting(layerCache, topLevelPicture, initialMat,
                             info, srcIR, dstIR, atlased, recycled, true, 0);
    }

}
Esempio n. 4
0
static SkBitmap unpremultiply_bitmap(const SkBitmap& bitmap,
                                     const SkIRect& srcRect) {
    SkBitmap outBitmap;
    outBitmap.allocPixels(bitmap.info().makeWH(srcRect.width(), srcRect.height()));
    int dstRow = 0;

    SkAutoLockPixels outBitmapPixelLock(outBitmap);
    SkAutoLockPixels bitmapPixelLock(bitmap);
    switch (bitmap.colorType()) {
        case kARGB_4444_SkColorType: {
            for (int y = srcRect.fTop; y < srcRect.fBottom; y++) {
                uint16_t* dst = outBitmap.getAddr16(0, dstRow);
                uint16_t* src = bitmap.getAddr16(0, y);
                for (int x = srcRect.fLeft; x < srcRect.fRight; x++) {
                    uint8_t a = SkGetPackedA4444(src[x]);
                    // It is necessary to average the color component of
                    // transparent pixels with their surrounding neighbors
                    // since the PDF renderer may separately re-sample the
                    // alpha and color channels when the image is not
                    // displayed at its native resolution. Since an alpha of
                    // zero gives no information about the color component,
                    // the pathological case is a white image with sharp
                    // transparency bounds - the color channel goes to black,
                    // and the should-be-transparent pixels are rendered
                    // as grey because of the separate soft mask and color
                    // resizing.
                    if (a == (SK_AlphaTRANSPARENT & 0x0F)) {
                        *dst = get_argb4444_neighbor_avg_color(bitmap, x, y);
                    } else {
                        *dst = remove_alpha_argb4444(src[x]);
                    }
                    dst++;
                }
                dstRow++;
            }
            break;
        }
        case kN32_SkColorType: {
            for (int y = srcRect.fTop; y < srcRect.fBottom; y++) {
                uint32_t* dst = outBitmap.getAddr32(0, dstRow);
                uint32_t* src = bitmap.getAddr32(0, y);
                for (int x = srcRect.fLeft; x < srcRect.fRight; x++) {
                    uint8_t a = SkGetPackedA32(src[x]);
                    if (a == SK_AlphaTRANSPARENT) {
                        *dst = get_argb8888_neighbor_avg_color(bitmap, x, y);
                    } else {
                        *dst = remove_alpha_argb8888(src[x]);
                    }
                    dst++;
                }
                dstRow++;
            }
            break;
        }
        default:
            SkASSERT(false);
    }

    outBitmap.setImmutable();

    return outBitmap;
}
Esempio n. 5
0
static SkIRect webRectToSkIRect(const WebRect& webRect)
{
    SkIRect irect;
    irect.set(webRect.x, webRect.y, webRect.x + webRect.width - 1, webRect.y + webRect.height - 1);
    return irect;
}
Esempio n. 6
0
static void draw_nine_clipped(const SkMask& mask, const SkIRect& outerR,
                              const SkIPoint& center, bool fillCenter,
                              const SkIRect& clipR, SkBlitter* blitter) {
    int cx = center.x();
    int cy = center.y();
    SkMask m;

    // top-left
    m.fBounds = mask.fBounds;
    m.fBounds.fRight = cx;
    m.fBounds.fBottom = cy;
    if (m.fBounds.width() > 0 && m.fBounds.height() > 0) {
        extractMaskSubset(mask, &m);
        m.fBounds.offsetTo(outerR.left(), outerR.top());
        blitClippedMask(blitter, m, m.fBounds, clipR);
    }

    // top-right
    m.fBounds = mask.fBounds;
    m.fBounds.fLeft = cx + 1;
    m.fBounds.fBottom = cy;
    if (m.fBounds.width() > 0 && m.fBounds.height() > 0) {
        extractMaskSubset(mask, &m);
        m.fBounds.offsetTo(outerR.right() - m.fBounds.width(), outerR.top());
        blitClippedMask(blitter, m, m.fBounds, clipR);
    }

    // bottom-left
    m.fBounds = mask.fBounds;
    m.fBounds.fRight = cx;
    m.fBounds.fTop = cy + 1;
    if (m.fBounds.width() > 0 && m.fBounds.height() > 0) {
        extractMaskSubset(mask, &m);
        m.fBounds.offsetTo(outerR.left(), outerR.bottom() - m.fBounds.height());
        blitClippedMask(blitter, m, m.fBounds, clipR);
    }

    // bottom-right
    m.fBounds = mask.fBounds;
    m.fBounds.fLeft = cx + 1;
    m.fBounds.fTop = cy + 1;
    if (m.fBounds.width() > 0 && m.fBounds.height() > 0) {
        extractMaskSubset(mask, &m);
        m.fBounds.offsetTo(outerR.right() - m.fBounds.width(),
                           outerR.bottom() - m.fBounds.height());
        blitClippedMask(blitter, m, m.fBounds, clipR);
    }

    SkIRect innerR;
    innerR.set(outerR.left() + cx - mask.fBounds.left(),
               outerR.top() + cy - mask.fBounds.top(),
               outerR.right() + (cx + 1 - mask.fBounds.right()),
               outerR.bottom() + (cy + 1 - mask.fBounds.bottom()));
    if (fillCenter) {
        blitClippedRect(blitter, innerR, clipR);
    }

    const int innerW = innerR.width();
    size_t storageSize = (innerW + 1) * (sizeof(int16_t) + sizeof(uint8_t));
    SkAutoSMalloc<4*1024> storage(storageSize);
    int16_t* runs = (int16_t*)storage.get();
    uint8_t* alpha = (uint8_t*)(runs + innerW + 1);

    SkIRect r;
    // top
    r.set(innerR.left(), outerR.top(), innerR.right(), innerR.top());
    if (r.intersect(clipR)) {
        int startY = SkMax32(0, r.top() - outerR.top());
        int stopY = startY + r.height();
        int width = r.width();
        for (int y = startY; y < stopY; ++y) {
            runs[0] = width;
            runs[width] = 0;
            alpha[0] = *mask.getAddr8(cx, mask.fBounds.top() + y);
            blitter->blitAntiH(r.left(), outerR.top() + y, alpha, runs);
        }
    }
    // bottom
    r.set(innerR.left(), innerR.bottom(), innerR.right(), outerR.bottom());
    if (r.intersect(clipR)) {
        int startY = outerR.bottom() - r.bottom();
        int stopY = startY + r.height();
        int width = r.width();
        for (int y = startY; y < stopY; ++y) {
            runs[0] = width;
            runs[width] = 0;
            alpha[0] = *mask.getAddr8(cx, mask.fBounds.bottom() - y - 1);
            blitter->blitAntiH(r.left(), outerR.bottom() - y - 1, alpha, runs);
        }
    }
    // left
    r.set(outerR.left(), innerR.top(), innerR.left(), innerR.bottom());
    if (r.intersect(clipR)) {
        int startX = r.left() - outerR.left();
        int stopX = startX + r.width();
        int height = r.height();
        for (int x = startX; x < stopX; ++x) {
            blitter->blitV(outerR.left() + x, r.top(), height,
                           *mask.getAddr8(mask.fBounds.left() + x, mask.fBounds.top() + cy));
        }
    }
    // right
    r.set(innerR.right(), innerR.top(), outerR.right(), innerR.bottom());
    if (r.intersect(clipR)) {
        int startX = outerR.right() - r.right();
        int stopX = startX + r.width();
        int height = r.height();
        for (int x = startX; x < stopX; ++x) {
            blitter->blitV(outerR.right() - x - 1, r.top(), height,
                           *mask.getAddr8(mask.fBounds.right() - x - 1, mask.fBounds.top() + cy));
        }
    }
}
Esempio n. 7
0
IntRect::IntRect(const SkIRect& r)
    : m_location(r.fLeft, r.fTop)
    , m_size(r.width(), r.height())
{
}
void GrVkGpuCommandBuffer::onClear(GrRenderTarget* target, const GrFixedClip& clip, GrColor color) {
    // parent class should never let us get here with no RT
    SkASSERT(target);
    SkASSERT(!clip.hasWindowRectangles());

    VkClearColorValue vkColor;
    GrColorToRGBAFloat(color, vkColor.float32);

    GrVkRenderTarget* vkRT = static_cast<GrVkRenderTarget*>(target);

    if (fIsEmpty && !clip.scissorEnabled()) {
        // We will change the render pass to do a clear load instead
        GrVkRenderPass::LoadStoreOps vkColorOps(VK_ATTACHMENT_LOAD_OP_CLEAR,
                                                VK_ATTACHMENT_STORE_OP_STORE);
        GrVkRenderPass::LoadStoreOps vkStencilOps(VK_ATTACHMENT_LOAD_OP_LOAD,
                                                  VK_ATTACHMENT_STORE_OP_STORE);

        const GrVkRenderPass* oldRP = fRenderPass;

        const GrVkResourceProvider::CompatibleRPHandle& rpHandle =
            vkRT->compatibleRenderPassHandle();
        if (rpHandle.isValid()) {
            fRenderPass = fGpu->resourceProvider().findRenderPass(rpHandle,
                                                                  vkColorOps,
                                                                  vkStencilOps);
        } else {
            fRenderPass = fGpu->resourceProvider().findRenderPass(*vkRT,
                                                                  vkColorOps,
                                                                  vkStencilOps);
        }

        SkASSERT(fRenderPass->isCompatible(*oldRP));
        oldRP->unref(fGpu);

        GrColorToRGBAFloat(color, fColorClearValue.color.float32);
        fStartsWithClear = true;
        return;
    }

    // We always do a sub rect clear with clearAttachments since we are inside a render pass
    VkClearRect clearRect;
    // Flip rect if necessary
    SkIRect vkRect;
    if (!clip.scissorEnabled()) {
        vkRect.setXYWH(0, 0, vkRT->width(), vkRT->height());
    } else if (kBottomLeft_GrSurfaceOrigin != vkRT->origin()) {
        vkRect = clip.scissorRect();
    } else {
        const SkIRect& scissor = clip.scissorRect();
        vkRect.setLTRB(scissor.fLeft, vkRT->height() - scissor.fBottom,
                       scissor.fRight, vkRT->height() - scissor.fTop);
    }
    clearRect.rect.offset = { vkRect.fLeft, vkRect.fTop };
    clearRect.rect.extent = { (uint32_t)vkRect.width(), (uint32_t)vkRect.height() };
    clearRect.baseArrayLayer = 0;
    clearRect.layerCount = 1;

    uint32_t colorIndex;
    SkAssertResult(fRenderPass->colorAttachmentIndex(&colorIndex));

    VkClearAttachment attachment;
    attachment.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
    attachment.colorAttachment = colorIndex;
    attachment.clearValue.color = vkColor;

    fCommandBuffer->clearAttachments(fGpu, 1, &attachment, 1, &clearRect);
    fIsEmpty = false;
    return;
}
Esempio n. 9
0
SkPDFImageShader::SkPDFImageShader(SkPDFShader::State* state) : fState(state) {
    fState.get()->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 = fState.get()->fCanvasTransform;
    finalMatrix.preConcat(fState.get()->fShaderTransform);
    SkRect deviceBounds;
    deviceBounds.set(fState.get()->fBBox);
    if (!inverseTransformBBox(finalMatrix, &deviceBounds)) {
        return;
    }

    const SkBitmap* image = &fState.get()->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] = fState.get()->fImageTileModes[0];
    tileModes[1] = fState.get()->fImageTileModes[1];
    if (tileModes[0] != SkShader::kClamp_TileMode ||
            tileModes[1] != SkShader::kClamp_TileMode) {
        deviceBounds.join(bitmapBounds);
    }

    SkMatrix unflip;
    unflip.setTranslate(0, SkScalarRoundToScalar(deviceBounds.height()));
    unflip.preScale(SK_Scalar1, -SK_Scalar1);
    SkISize size = SkISize::Make(SkScalarRound(deviceBounds.width()),
                                 SkScalarRound(deviceBounds.height()));
    SkPDFDevice pattern(size, size, unflip);
    SkCanvas canvas(&pattern);

    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);
        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(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);
            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 (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);
            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 = 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());
            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 (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);
            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 = deviceBounds.height();
        }
    }

    // Put the canvas into the pattern stream (fContent).
    SkAutoTUnref<SkStream> content(pattern.content());
    setData(content.get());
    SkPDFResourceDict* resourceDict = pattern.getResourceDict();
    resourceDict->getReferencedResources(fResources, &fResources, false);

    populate_tiling_pattern_dict(this, patternBBox,
                                 pattern.getResourceDict(), finalMatrix);

    fState.get()->fImage.unlockPixels();
}
Esempio n. 10
0
bool GrDrawTarget::setupDstReadIfNecessary(const GrPipelineBuilder& pipelineBuilder,
                                           const GrProcOptInfo& colorPOI,
                                           const GrProcOptInfo& coveragePOI,
                                           GrXferProcessor::DstTexture* dstTexture,
                                           const SkRect* drawBounds) {
    if (!pipelineBuilder.willXPNeedDstTexture(*this->caps(), colorPOI, coveragePOI)) {
        return true;
    }

    GrRenderTarget* rt = pipelineBuilder.getRenderTarget();

    if (this->caps()->textureBarrierSupport()) {
        if (GrTexture* rtTex = rt->asTexture()) {
            // The render target is a texture, so we can read from it directly in the shader. The XP
            // will be responsible to detect this situation and request a texture barrier.
            dstTexture->setTexture(rtTex);
            dstTexture->setOffset(0, 0);
            return true;
        }
    }

    SkIRect copyRect;
    pipelineBuilder.clip().getConservativeBounds(rt, &copyRect);

    if (drawBounds) {
        SkIRect drawIBounds;
        drawBounds->roundOut(&drawIBounds);
        if (!copyRect.intersect(drawIBounds)) {
#ifdef SK_DEBUG
            GrCapsDebugf(fCaps, "Missed an early reject. "
                         "Bailing on draw from setupDstReadIfNecessary.\n");
#endif
            return false;
        }
    } else {
#ifdef SK_DEBUG
        //SkDebugf("No dev bounds when dst copy is made.\n");
#endif
    }

    // MSAA consideration: When there is support for reading MSAA samples in the shader we could
    // have per-sample dst values by making the copy multisampled.
    GrSurfaceDesc desc;
    if (!this->getGpu()->initCopySurfaceDstDesc(rt, &desc)) {
        desc.fOrigin = kDefault_GrSurfaceOrigin;
        desc.fFlags = kRenderTarget_GrSurfaceFlag;
        desc.fConfig = rt->config();
    }


    desc.fWidth = copyRect.width();
    desc.fHeight = copyRect.height();

    SkAutoTUnref<GrTexture> copy(
        fResourceProvider->refScratchTexture(desc, GrTextureProvider::kApprox_ScratchTexMatch));

    if (!copy) {
        SkDebugf("Failed to create temporary copy of destination texture.\n");
        return false;
    }
    SkIPoint dstPoint = {0, 0};
    this->copySurface(copy, rt, copyRect, dstPoint);
    dstTexture->setTexture(copy);
    dstTexture->setOffset(copyRect.fLeft, copyRect.fTop);
    return true;
}
Esempio n. 11
0
bool GrSWMaskHelper::init(const SkIRect& resultBounds,
                          const SkMatrix* matrix,
                          bool allowCompression) {
    if (matrix) {
        fMatrix = *matrix;
    } else {
        fMatrix.setIdentity();
    }

    // Now translate so the bound's UL corner is at the origin
    fMatrix.postTranslate(-resultBounds.fLeft * SK_Scalar1,
                          -resultBounds.fTop * SK_Scalar1);
    SkIRect bounds = SkIRect::MakeWH(resultBounds.width(),
                                     resultBounds.height());

    if (allowCompression &&
        fContext->caps()->drawPathMasksToCompressedTexturesSupport() &&
        choose_compressed_fmt(fContext->caps(), &fCompressedFormat)) {
        fCompressionMode = kCompress_CompressionMode;
    }

    // Make sure that the width is a multiple of the desired block dimensions
    // to allow for specialized SIMD instructions that compress multiple blocks at a time.
    int cmpWidth = bounds.fRight;
    int cmpHeight = bounds.fBottom;
    if (kCompress_CompressionMode == fCompressionMode) {
        int dimX, dimY;
        SkTextureCompressor::GetBlockDimensions(fCompressedFormat, &dimX, &dimY);
        cmpWidth = dimX * ((cmpWidth + (dimX - 1)) / dimX);
        cmpHeight = dimY * ((cmpHeight + (dimY - 1)) / dimY);

        // Can we create a blitter?
        if (SkTextureCompressor::ExistsBlitterForFormat(fCompressedFormat)) {
            int cmpSz = SkTextureCompressor::GetCompressedDataSize(
                fCompressedFormat, cmpWidth, cmpHeight);

            SkASSERT(cmpSz > 0);
            SkASSERT(nullptr == fCompressedBuffer.get());
            fCompressedBuffer.reset(cmpSz);
            fCompressionMode = kBlitter_CompressionMode;
        }
    } 

    sk_bzero(&fDraw, sizeof(fDraw));

    // If we don't have a custom blitter, then we either need a bitmap to compress
    // from or a bitmap that we're going to use as a texture. In any case, we should
    // allocate the pixels for a bitmap
    const SkImageInfo bmImageInfo = SkImageInfo::MakeA8(cmpWidth, cmpHeight);
    if (kBlitter_CompressionMode != fCompressionMode) {
        if (!fPixels.tryAlloc(bmImageInfo)) {
            return false;
        }
        fPixels.erase(0);
    } else {
        // Otherwise, we just need to remember how big the buffer is...
        fPixels.reset(bmImageInfo);
    }
    fDraw.fDst      = fPixels;
    fRasterClip.setRect(bounds);
    fDraw.fRC       = &fRasterClip;
    fDraw.fClip     = &fRasterClip.bwRgn();
    fDraw.fMatrix   = &fMatrix;
    return true;
}
Esempio n. 12
0
SkPDFShader::State::State(const SkShader& shader, const SkMatrix& canvasTransform,
                          const SkIRect& bbox, SkScalar rasterScale)
        : fCanvasTransform(canvasTransform),
          fBBox(bbox),
          fPixelGeneration(0) {
    fInfo.fColorCount = 0;
    fInfo.fColors = NULL;
    fInfo.fColorOffsets = NULL;
    fShaderTransform = shader.getLocalMatrix();
    fImageTileModes[0] = fImageTileModes[1] = SkShader::kClamp_TileMode;

    fType = shader.asAGradient(&fInfo);

    if (fType == SkShader::kNone_GradientType) {
        SkMatrix matrix;
        if (shader.isABitmap(&fImage, &matrix, fImageTileModes)) {
            SkASSERT(matrix.isIdentity());
        } else {
            // Generic fallback for unsupported shaders:
            //  * allocate a bbox-sized bitmap
            //  * shade the whole area
            //  * use the result as a bitmap shader

            // bbox is in device space. While that's exactly what we want for sizing our bitmap,
            // we need to map it into shader space for adjustments (to match
            // SkPDFImageShader::Create's behavior).
            SkRect shaderRect = SkRect::Make(bbox);
            if (!inverse_transform_bbox(canvasTransform, &shaderRect)) {
                fImage.reset();
                return;
            }

            // Clamp the bitmap size to about 1M pixels
            static const SkScalar kMaxBitmapArea = 1024 * 1024;
            SkScalar bitmapArea = rasterScale * bbox.width() * rasterScale * bbox.height();
            if (bitmapArea > kMaxBitmapArea) {
                rasterScale *= SkScalarSqrt(kMaxBitmapArea / bitmapArea);
            }

            SkISize size = SkISize::Make(SkScalarRoundToInt(rasterScale * bbox.width()),
                                         SkScalarRoundToInt(rasterScale * bbox.height()));
            SkSize scale = SkSize::Make(SkIntToScalar(size.width()) / shaderRect.width(),
                                        SkIntToScalar(size.height()) / shaderRect.height());

            fImage.allocN32Pixels(size.width(), size.height());
            fImage.eraseColor(SK_ColorTRANSPARENT);

            SkPaint p;
            p.setShader(const_cast<SkShader*>(&shader));

            SkCanvas canvas(fImage);
            canvas.scale(scale.width(), scale.height());
            canvas.translate(-shaderRect.x(), -shaderRect.y());
            canvas.drawPaint(p);

            fShaderTransform.setTranslate(shaderRect.x(), shaderRect.y());
            fShaderTransform.preScale(1 / scale.width(), 1 / scale.height());
        }
        fPixelGeneration = fImage.getGenerationID();
    } else {
        AllocateGradientInfoStorage();
        shader.asAGradient(&fInfo);
    }
}
Esempio n. 13
0
 void build_base_rgn(SkRegion* rgn) {
     rgn->setRect(fBase);
     SkIRect r = fBase;
     r.offset(75, 20);
     rgn->op(r, SkRegion::kUnion_Op);
 }
Esempio n. 14
0
void TiledPage::prepare(bool goingDown, bool goingLeft, const SkIRect& tileBounds, PrepareBounds bounds)
{
    if (!m_glWebViewState)
        return;

    TilesManager::instance()->gatherTextures();
    m_scrollingDown = goingDown;

    int firstTileX = tileBounds.fLeft;
    int firstTileY = tileBounds.fTop;
    int nbTilesWidth = tileBounds.width();
    int nbTilesHeight = tileBounds.height();

    int lastTileX = tileBounds.fRight - 1;
    int lastTileY = tileBounds.fBottom - 1;

    // Expand number of tiles to allow tiles outside of viewport to be prepared for
    // smoother scrolling.
    int nTilesToPrepare = nbTilesWidth * nbTilesHeight;
    int nMaxTilesPerPage = m_baseTileSize / 2;

    if (bounds == ExpandedBounds) {
        // prepare tiles outside of the visible bounds
        int expandX = m_glWebViewState->expandedTileBoundsX();
        int expandY = m_glWebViewState->expandedTileBoundsY();

        firstTileX -= expandX;
        lastTileX += expandX;
        nbTilesWidth += expandX * 2;

        firstTileY -= expandY;
        lastTileY += expandY;
        nbTilesHeight += expandY * 2;
    }

    // crop the prepared region to the contents of the base layer
    float maxWidthTiles = m_glWebViewState->baseContentWidth() * m_scale / TilesManager::tileWidth();
    float maxHeightTiles = m_glWebViewState->baseContentHeight() * m_scale / TilesManager::tileHeight();
    firstTileX = std::max(0, firstTileX);
    firstTileY = std::max(0, firstTileY);
    lastTileX = std::min(lastTileX, static_cast<int>(ceilf(maxWidthTiles)) - 1);
    lastTileY = std::min(lastTileY, static_cast<int>(ceilf(maxHeightTiles)) - 1);

    m_expandedTileBounds.fLeft = firstTileX;
    m_expandedTileBounds.fTop = firstTileY;
    m_expandedTileBounds.fRight = lastTileX;
    m_expandedTileBounds.fBottom = lastTileY;

    // check against corrupted scale values giving bad height/width (use float to avoid overflow)
    float numTiles = static_cast<float>(nbTilesHeight) * static_cast<float>(nbTilesWidth);
    if (numTiles > TilesManager::getMaxTextureAllocation() || nbTilesHeight < 1 || nbTilesWidth < 1)
    {
        XLOGC("ERROR: We don't have enough tiles for this page!"
              " nbTilesHeight %d nbTilesWidth %d", nbTilesHeight, nbTilesWidth);
        return;
    }
    for (int i = 0; i < nbTilesHeight; i++)
        prepareRow(goingLeft, nbTilesWidth, firstTileX, firstTileY + i, tileBounds);

    m_prepare = true;
}
Esempio n. 15
0
bool SkBicubicImageFilter::onFilterImage(Proxy* proxy,
                                         const SkBitmap& source,
                                         const SkMatrix& matrix,
                                         SkBitmap* result,
                                         SkIPoint* loc) {
    SkBitmap src = source;
    if (getInput(0) && !getInput(0)->filterImage(proxy, source, matrix, &src, loc)) {
        return false;
    }

    if (src.config() != SkBitmap::kARGB_8888_Config) {
        return false;
    }

    SkAutoLockPixels alp(src);
    if (!src.getPixels()) {
        return false;
    }

    SkRect dstRect = SkRect::MakeWH(SkScalarMul(SkIntToScalar(src.width()), fScale.fWidth),
                                    SkScalarMul(SkIntToScalar(src.height()), fScale.fHeight));
    SkIRect dstIRect;
    dstRect.roundOut(&dstIRect);
    if (dstIRect.isEmpty()) {
        return false;
    }
    result->setConfig(src.config(), dstIRect.width(), dstIRect.height());
    result->allocPixels();
    if (!result->getPixels()) {
        return false;
    }

    SkRect srcRect;
    src.getBounds(&srcRect);
    SkMatrix inverse;
    inverse.setRectToRect(dstRect, srcRect, SkMatrix::kFill_ScaleToFit);
    inverse.postTranslate(SkFloatToScalar(-0.5f), SkFloatToScalar(-0.5f));

    for (int y = dstIRect.fTop; y < dstIRect.fBottom; ++y) {
        SkPMColor* dptr = result->getAddr32(dstIRect.fLeft, y);
        for (int x = dstIRect.fLeft; x < dstIRect.fRight; ++x) {
            SkPoint srcPt, dstPt = SkPoint::Make(SkIntToScalar(x), SkIntToScalar(y));
            inverse.mapPoints(&srcPt, &dstPt, 1);
            SkScalar fractx = srcPt.fX - SkScalarFloorToScalar(srcPt.fX);
            SkScalar fracty = srcPt.fY - SkScalarFloorToScalar(srcPt.fY);
            int sx = SkScalarFloorToInt(srcPt.fX);
            int sy = SkScalarFloorToInt(srcPt.fY);
            int x0 = SkClampMax(sx - 1, src.width() - 1);
            int x1 = SkClampMax(sx    , src.width() - 1);
            int x2 = SkClampMax(sx + 1, src.width() - 1);
            int x3 = SkClampMax(sx + 2, src.width() - 1);
            int y0 = SkClampMax(sy - 1, src.height() - 1);
            int y1 = SkClampMax(sy    , src.height() - 1);
            int y2 = SkClampMax(sy + 1, src.height() - 1);
            int y3 = SkClampMax(sy + 2, src.height() - 1);
            SkPMColor s00 = *src.getAddr32(x0, y0);
            SkPMColor s10 = *src.getAddr32(x1, y0);
            SkPMColor s20 = *src.getAddr32(x2, y0);
            SkPMColor s30 = *src.getAddr32(x3, y0);
            SkPMColor s0 = cubicBlend(fCoefficients, fractx, s00, s10, s20, s30);
            SkPMColor s01 = *src.getAddr32(x0, y1);
            SkPMColor s11 = *src.getAddr32(x1, y1);
            SkPMColor s21 = *src.getAddr32(x2, y1);
            SkPMColor s31 = *src.getAddr32(x3, y1);
            SkPMColor s1 = cubicBlend(fCoefficients, fractx, s01, s11, s21, s31);
            SkPMColor s02 = *src.getAddr32(x0, y2);
            SkPMColor s12 = *src.getAddr32(x1, y2);
            SkPMColor s22 = *src.getAddr32(x2, y2);
            SkPMColor s32 = *src.getAddr32(x3, y2);
            SkPMColor s2 = cubicBlend(fCoefficients, fractx, s02, s12, s22, s32);
            SkPMColor s03 = *src.getAddr32(x0, y3);
            SkPMColor s13 = *src.getAddr32(x1, y3);
            SkPMColor s23 = *src.getAddr32(x2, y3);
            SkPMColor s33 = *src.getAddr32(x3, y3);
            SkPMColor s3 = cubicBlend(fCoefficients, fractx, s03, s13, s23, s33);
            *dptr++ = cubicBlend(fCoefficients, fracty, s0, s1, s2, s3);
        }
    }
    return true;
}
Esempio n. 16
0
bool CopyTilesRenderer::render(SkBitmap** out) {
    int i = 0;
    bool success = true;
    SkBitmap dst;
    for (int x = 0; x < this->getViewWidth(); x += fLargeTileWidth) {
        for (int y = 0; y < this->getViewHeight(); y += fLargeTileHeight) {
            SkAutoCanvasRestore autoRestore(fCanvas, true);
            // Translate so that we draw the correct portion of the picture.
            // Perform a postTranslate so that the scaleFactor does not interfere with the
            // positioning.
            SkMatrix mat(fCanvas->getTotalMatrix());
            mat.postTranslate(SkIntToScalar(-x), SkIntToScalar(-y));
            fCanvas->setMatrix(mat);
            // Draw the picture
            if (fUseMultiPictureDraw) {
                SkMultiPictureDraw mpd;

                mpd.add(fCanvas, fPicture);

                mpd.draw();
            } else {
                fCanvas->drawPicture(fPicture);
            }
            // Now extract the picture into tiles
            SkBitmap baseBitmap;
            fCanvas->readPixels(SkIRect::MakeSize(fCanvas->getBaseLayerSize()), &baseBitmap);
            SkIRect subset;
            for (int tileY = 0; tileY < fLargeTileHeight; tileY += this->getTileHeight()) {
                for (int tileX = 0; tileX < fLargeTileWidth; tileX += this->getTileWidth()) {
                    subset.set(tileX, tileY, tileX + this->getTileWidth(),
                               tileY + this->getTileHeight());
                    SkDEBUGCODE(bool extracted =)
                    baseBitmap.extractSubset(&dst, subset);
                    SkASSERT(extracted);
                    if (!fWritePath.isEmpty()) {
                        // Similar to write() in PictureRenderer.cpp, but just encodes
                        // a bitmap directly.
                        // TODO: Share more common code with write() to do this, to properly
                        // write out the JSON summary, etc.
                        SkString pathWithNumber = SkOSPath::Join(fWritePath.c_str(),
                                                  fInputFilename.c_str());
                        pathWithNumber.remove(pathWithNumber.size() - 4, 4);
                        pathWithNumber.appendf("%i.png", i++);
                        SkBitmap copy;
#if SK_SUPPORT_GPU
                        if (isUsingGpuDevice()) {
                            dst.pixelRef()->readPixels(&copy, &subset);
                        } else {
#endif
                            dst.copyTo(&copy);
#if SK_SUPPORT_GPU
                        }
#endif
                        success &= SkImageEncoder::EncodeFile(pathWithNumber.c_str(), copy,
                                                              SkImageEncoder::kPNG_Type, 100);
                    }
                }
            }
        }
    }
    return success;
}
Esempio n. 17
0
static void blitClippedRect(SkBlitter* blitter, const SkIRect& rect, const SkIRect& clipR) {
    SkIRect r;
    if (r.intersect(rect, clipR)) {
        blitter->blitRect(r.left(), r.top(), r.width(), r.height());
    }
}
Esempio n. 18
0
void draw(SkCanvas* canvas) {
    SkIRect result;
    bool intersected = result.intersect({ 10, 40, 50, 80 }, { 30, 60, 70, 90 });
    SkDebugf("%s intersection: %d, %d, %d, %d\n", intersected ? "" : "no ",
             result.left(), result.top(), result.right(), result.bottom());
}
Esempio n. 19
0
void draw(SkCanvas* canvas) {
    SkIRect leftRect =  { 10, 40, 50, 80 };
    SkDebugf("%s intersection: ", leftRect.intersect(30, 60, 70, 90) ? "" : "no ");
    SkDebugf("%d, %d, %d, %d\n", leftRect.left(), leftRect.top(),
                                 leftRect.right(), leftRect.bottom());
}
sk_sp<SkSpecialImage> SkMagnifierImageFilter::onFilterImage(SkSpecialImage* source,
                                                            const Context& ctx,
                                                            SkIPoint* offset) const {
    SkIPoint inputOffset = SkIPoint::Make(0, 0);
    sk_sp<SkSpecialImage> input(this->filterInput(0, source, ctx, &inputOffset));
    if (!input) {
        return nullptr;
    }

    const SkIRect inputBounds = SkIRect::MakeXYWH(inputOffset.x(), inputOffset.y(),
                                                  input->width(), input->height());

    SkIRect bounds;
    if (!this->applyCropRect(ctx, inputBounds, &bounds)) {
        return nullptr;
    }

    SkScalar invInset = fInset > 0 ? SkScalarInvert(fInset) : SK_Scalar1;

    SkScalar invXZoom = fSrcRect.width() / bounds.width();
    SkScalar invYZoom = fSrcRect.height() / bounds.height();


#if SK_SUPPORT_GPU
    if (source->isTextureBacked()) {
        GrContext* context = source->getContext();

        sk_sp<GrTexture> inputTexture(input->asTextureRef(context));
        SkASSERT(inputTexture);

        offset->fX = bounds.left();
        offset->fY = bounds.top();
        bounds.offset(-inputOffset);

        SkScalar yOffset = inputTexture->origin() == kTopLeft_GrSurfaceOrigin 
            ? fSrcRect.y()
            : inputTexture->height() - 
                      fSrcRect.height() * inputTexture->height() / bounds.height() - fSrcRect.y();
        int boundsY = inputTexture->origin() == kTopLeft_GrSurfaceOrigin
            ? bounds.y()
            : inputTexture->height() - bounds.height();
        SkRect effectBounds = SkRect::MakeXYWH(
            SkIntToScalar(bounds.x()) / inputTexture->width(),
            SkIntToScalar(boundsY) / inputTexture->height(),
            SkIntToScalar(inputTexture->width()) / bounds.width(),
            SkIntToScalar(inputTexture->height()) / bounds.height());
        // SRGBTODO: Handle sRGB here
        sk_sp<GrFragmentProcessor> fp(GrMagnifierEffect::Create(
                                                        inputTexture.get(),
                                                        effectBounds,
                                                        fSrcRect.x() / inputTexture->width(),
                                                        yOffset / inputTexture->height(),
                                                        invXZoom,
                                                        invYZoom,
                                                        bounds.width() * invInset,
                                                        bounds.height() * invInset));
        if (!fp) {
            return nullptr;
        }

        return DrawWithFP(context, std::move(fp), bounds);
    }
#endif

    SkBitmap inputBM;

    if (!input->getROPixels(&inputBM)) {
        return nullptr;
    }

    if ((inputBM.colorType() != kN32_SkColorType) ||
        (fSrcRect.width() >= inputBM.width()) || (fSrcRect.height() >= inputBM.height())) {
        return nullptr;
    }

    SkAutoLockPixels alp(inputBM);
    SkASSERT(inputBM.getPixels());
    if (!inputBM.getPixels() || inputBM.width() <= 0 || inputBM.height() <= 0) {
        return nullptr;
    }

    const SkImageInfo info = SkImageInfo::MakeN32Premul(bounds.width(), bounds.height());

    SkBitmap dst;
    if (!dst.tryAllocPixels(info)) {
        return nullptr;
    }

    SkAutoLockPixels dstLock(dst);

    SkColor* dptr = dst.getAddr32(0, 0);
    int dstWidth = dst.width(), dstHeight = dst.height();
    for (int y = 0; y < dstHeight; ++y) {
        for (int x = 0; x < dstWidth; ++x) {
            SkScalar x_dist = SkMin32(x, dstWidth - x - 1) * invInset;
            SkScalar y_dist = SkMin32(y, dstHeight - y - 1) * invInset;
            SkScalar weight = 0;

            static const SkScalar kScalar2 = SkScalar(2);

            // To create a smooth curve at the corners, we need to work on
            // a square twice the size of the inset.
            if (x_dist < kScalar2 && y_dist < kScalar2) {
                x_dist = kScalar2 - x_dist;
                y_dist = kScalar2 - y_dist;

                SkScalar dist = SkScalarSqrt(SkScalarSquare(x_dist) +
                                             SkScalarSquare(y_dist));
                dist = SkMaxScalar(kScalar2 - dist, 0);
                weight = SkMinScalar(SkScalarSquare(dist), SK_Scalar1);
            } else {
                SkScalar sqDist = SkMinScalar(SkScalarSquare(x_dist),
                                              SkScalarSquare(y_dist));
                weight = SkMinScalar(sqDist, SK_Scalar1);
            }

            SkScalar x_interp = SkScalarMul(weight, (fSrcRect.x() + x * invXZoom)) +
                           (SK_Scalar1 - weight) * x;
            SkScalar y_interp = SkScalarMul(weight, (fSrcRect.y() + y * invYZoom)) +
                           (SK_Scalar1 - weight) * y;

            int x_val = SkTPin(bounds.x() + SkScalarFloorToInt(x_interp), 0, inputBM.width() - 1);
            int y_val = SkTPin(bounds.y() + SkScalarFloorToInt(y_interp), 0, inputBM.height() - 1);

            *dptr = *inputBM.getAddr32(x_val, y_val);
            dptr++;
        }
    }

    offset->fX = bounds.left();
    offset->fY = bounds.top();
    return SkSpecialImage::MakeFromRaster(SkIRect::MakeWH(bounds.width(), bounds.height()),
                                          dst);
}
Esempio n. 21
0
/**
 *  Clip restriction logic exists in the canvas itself, and in various kinds of devices.
 *
 *  This test explicitly tries to exercise that variety:
 *  - picture : empty device but exercises canvas itself
 *  - pdf : uses SkClipStack in its device (as does SVG and GPU)
 *  - raster : uses SkRasterClip in its device
 */
DEF_TEST(canvas_clip_restriction, reporter) {
    multi_canvas_driver(gBaseRestrictedR.width(), gBaseRestrictedR.height(),
                        [reporter](SkCanvas* canvas) { test_restriction(reporter, canvas); });
}
////////////////////////////////////////////////////////////////////////////////
// sort out what kind of clip mask needs to be created: alpha, stencil,
// scissor, or entirely software
bool GrClipMaskManager::setupClipping(GrPipelineBuilder* pipelineBuilder,
                                      GrPipelineBuilder::AutoRestoreFragmentProcessors* arfp,
                                      GrPipelineBuilder::AutoRestoreStencil* ars,
                                      GrScissorState* scissorState,
                                      const SkRect* devBounds) {
    fCurrClipMaskType = kNone_ClipMaskType;
    if (kRespectClip_StencilClipMode == fClipMode) {
        fClipMode = kIgnoreClip_StencilClipMode;
    }

    GrReducedClip::ElementList elements(16);
    int32_t genID = 0;
    GrReducedClip::InitialState initialState = GrReducedClip::kAllIn_InitialState;
    SkIRect clipSpaceIBounds;
    bool requiresAA = false;
    GrRenderTarget* rt = pipelineBuilder->getRenderTarget();

    // GrDrawTarget should have filtered this for us
    SkASSERT(rt);

    SkIRect clipSpaceRTIBounds = SkIRect::MakeWH(rt->width(), rt->height());
    const GrClip& clip = pipelineBuilder->clip();
    if (clip.isWideOpen(clipSpaceRTIBounds)) {
        this->setPipelineBuilderStencil(pipelineBuilder, ars);
        return true;
    }

    // The clip mask manager always draws with a single IRect so we special case that logic here
    // Image filters just use a rect, so we also special case that logic
    switch (clip.clipType()) {
    case GrClip::kWideOpen_ClipType:
        SkFAIL("Should have caught this with clip.isWideOpen()");
        return true;
    case GrClip::kIRect_ClipType: {
        SkIRect scissor = clip.irect();
        if (scissor.intersect(clipSpaceRTIBounds)) {
            scissorState->set(scissor);
            this->setPipelineBuilderStencil(pipelineBuilder, ars);
            return true;
        }
        return false;
    }
    case GrClip::kClipStack_ClipType: {
        clipSpaceRTIBounds.offset(clip.origin());
        GrReducedClip::ReduceClipStack(*clip.clipStack(),
                                       clipSpaceRTIBounds,
                                       &elements,
                                       &genID,
                                       &initialState,
                                       &clipSpaceIBounds,
                                       &requiresAA);
        if (elements.isEmpty()) {
            if (GrReducedClip::kAllIn_InitialState == initialState) {
                if (clipSpaceIBounds == clipSpaceRTIBounds) {
                    this->setPipelineBuilderStencil(pipelineBuilder, ars);
                    return true;
                }
            } else {
                return false;
            }
        }
    }
    break;
    }

    // An element count of 4 was chosen because of the common pattern in Blink of:
    //   isect RR
    //   diff  RR
    //   isect convex_poly
    //   isect convex_poly
    // when drawing rounded div borders. This could probably be tuned based on a
    // configuration's relative costs of switching RTs to generate a mask vs
    // longer shaders.
    if (elements.count() <= 4) {
        SkVector clipToRTOffset = { SkIntToScalar(-clip.origin().fX),
                                    SkIntToScalar(-clip.origin().fY)
                                  };
        if (elements.isEmpty() ||
                (requiresAA && this->installClipEffects(pipelineBuilder, arfp, elements, clipToRTOffset,
                        devBounds))) {
            SkIRect scissorSpaceIBounds(clipSpaceIBounds);
            scissorSpaceIBounds.offset(-clip.origin());
            if (NULL == devBounds ||
                    !SkRect::Make(scissorSpaceIBounds).contains(*devBounds)) {
                scissorState->set(scissorSpaceIBounds);
            }
            this->setPipelineBuilderStencil(pipelineBuilder, ars);
            return true;
        }
    }

    // If MSAA is enabled we can do everything in the stencil buffer.
    if (0 == rt->numSamples() && requiresAA) {
        GrTexture* result = NULL;

        // The top-left of the mask corresponds to the top-left corner of the bounds.
        SkVector clipToMaskOffset = {
            SkIntToScalar(-clipSpaceIBounds.fLeft),
            SkIntToScalar(-clipSpaceIBounds.fTop)
        };

        if (this->useSWOnlyPath(pipelineBuilder, clipToMaskOffset, elements)) {
            // The clip geometry is complex enough that it will be more efficient to create it
            // entirely in software
            result = this->createSoftwareClipMask(genID,
                                                  initialState,
                                                  elements,
                                                  clipToMaskOffset,
                                                  clipSpaceIBounds);
        } else {
            result = this->createAlphaClipMask(genID,
                                               initialState,
                                               elements,
                                               clipToMaskOffset,
                                               clipSpaceIBounds);
        }

        if (result) {
            arfp->set(pipelineBuilder);
            // The mask's top left coord should be pinned to the rounded-out top left corner of
            // clipSpace bounds. We determine the mask's position WRT to the render target here.
            SkIRect rtSpaceMaskBounds = clipSpaceIBounds;
            rtSpaceMaskBounds.offset(-clip.origin());
            setup_drawstate_aaclip(pipelineBuilder, result, arfp, rtSpaceMaskBounds);
            this->setPipelineBuilderStencil(pipelineBuilder, ars);
            return true;
        }
        // if alpha clip mask creation fails fall through to the non-AA code paths
    }

    // Either a hard (stencil buffer) clip was explicitly requested or an anti-aliased clip couldn't
    // be created. In either case, free up the texture in the anti-aliased mask cache.
    // TODO: this may require more investigation. Ganesh performs a lot of utility draws (e.g.,
    // clears, InOrderDrawBuffer playbacks) that hit the stencil buffer path. These may be
    // "incorrectly" clearing the AA cache.
    fAACache.reset();

    // use the stencil clip if we can't represent the clip as a rectangle.
    SkIPoint clipSpaceToStencilSpaceOffset = -clip.origin();
    this->createStencilClipMask(rt,
                                genID,
                                initialState,
                                elements,
                                clipSpaceIBounds,
                                clipSpaceToStencilSpaceOffset);

    // This must occur after createStencilClipMask. That function may change the scissor. Also, it
    // only guarantees that the stencil mask is correct within the bounds it was passed, so we must
    // use both stencil and scissor test to the bounds for the final draw.
    SkIRect scissorSpaceIBounds(clipSpaceIBounds);
    scissorSpaceIBounds.offset(clipSpaceToStencilSpaceOffset);
    scissorState->set(scissorSpaceIBounds);
    this->setPipelineBuilderStencil(pipelineBuilder, ars);
    return true;
}
Esempio n. 23
0
static void toString(const SkIRect& r, SkString* str) {
    str->appendf("[%d,%d %d:%d]", r.fLeft, r.fTop, r.width(), r.height());
}
Esempio n. 24
0
static void check(skiatest::Reporter* r,
                  const char path[],
                  SkISize size,
                  bool supportsScanlineDecoding,
                  bool supportsSubsetDecoding,
                  bool supports565 = true,
                  bool supportsIncomplete = true) {

    SkAutoTDelete<SkStream> stream(resource(path));
    if (!stream) {
        SkDebugf("Missing resource '%s'\n", path);
        return;
    }

    SkAutoTDelete<SkCodec> codec(nullptr);
    bool isIncomplete = supportsIncomplete;
    if (isIncomplete) {
        size_t size = stream->getLength();
        SkAutoTUnref<SkData> data((SkData::NewFromStream(stream, 2 * size / 3)));
        codec.reset(SkCodec::NewFromData(data));
    } else {
        codec.reset(SkCodec::NewFromStream(stream.detach()));
    }
    if (!codec) {
        ERRORF(r, "Unable to decode '%s'", path);
        return;
    }

    // Test full image decodes with SkCodec
    SkMD5::Digest codecDigest;
    SkImageInfo info = codec->getInfo().makeColorType(kN32_SkColorType);
    SkBitmap bm;
    SkCodec::Result expectedResult = isIncomplete ? SkCodec::kIncompleteInput : SkCodec::kSuccess;
    test_codec(r, codec, bm, info, size, supports565, expectedResult, &codecDigest, nullptr);

    // Scanline decoding follows.
    // Need to call startScanlineDecode() first.
    REPORTER_ASSERT(r, codec->getScanlines(bm.getAddr(0, 0), 1, 0)
            == 0);
    REPORTER_ASSERT(r, codec->skipScanlines(1)
            == 0);

    const SkCodec::Result startResult = codec->startScanlineDecode(info);
    if (supportsScanlineDecoding) {
        bm.eraseColor(SK_ColorYELLOW);

        REPORTER_ASSERT(r, startResult == SkCodec::kSuccess);

        for (int y = 0; y < info.height(); y++) {
            const int lines = codec->getScanlines(bm.getAddr(0, y), 1, 0);
            if (!isIncomplete) {
                REPORTER_ASSERT(r, 1 == lines);
            }
        }
        // verify that scanline decoding gives the same result.
        if (SkCodec::kTopDown_SkScanlineOrder == codec->getScanlineOrder()) {
            compare_to_good_digest(r, codecDigest, bm);
        }

        // Cannot continue to decode scanlines beyond the end
        REPORTER_ASSERT(r, codec->getScanlines(bm.getAddr(0, 0), 1, 0)
                == 0);

        // Interrupting a scanline decode with a full decode starts from
        // scratch
        REPORTER_ASSERT(r, codec->startScanlineDecode(info) == SkCodec::kSuccess);
        const int lines = codec->getScanlines(bm.getAddr(0, 0), 1, 0);
        if (!isIncomplete) {
            REPORTER_ASSERT(r, lines == 1);
        }
        REPORTER_ASSERT(r, codec->getPixels(bm.info(), bm.getPixels(), bm.rowBytes())
                == expectedResult);
        REPORTER_ASSERT(r, codec->getScanlines(bm.getAddr(0, 0), 1, 0)
                == 0);
        REPORTER_ASSERT(r, codec->skipScanlines(1)
                == 0);
    } else {
        REPORTER_ASSERT(r, startResult == SkCodec::kUnimplemented);
    }

    // The rest of this function tests decoding subsets, and will decode an arbitrary number of
    // random subsets.
    // Do not attempt to decode subsets of an image of only once pixel, since there is no
    // meaningful subset.
    if (size.width() * size.height() == 1) {
        return;
    }

    SkRandom rand;
    SkIRect subset;
    SkCodec::Options opts;
    opts.fSubset = &subset;
    for (int i = 0; i < 5; i++) {
        subset = generate_random_subset(&rand, size.width(), size.height());
        SkASSERT(!subset.isEmpty());
        const bool supported = codec->getValidSubset(&subset);
        REPORTER_ASSERT(r, supported == supportsSubsetDecoding);

        SkImageInfo subsetInfo = info.makeWH(subset.width(), subset.height());
        SkBitmap bm;
        bm.allocPixels(subsetInfo);
        const SkCodec::Result result = codec->getPixels(bm.info(), bm.getPixels(), bm.rowBytes(),
                                                        &opts, nullptr, nullptr);

        if (supportsSubsetDecoding) {
            REPORTER_ASSERT(r, result == expectedResult);
            // Webp is the only codec that supports subsets, and it will have modified the subset
            // to have even left/top.
            REPORTER_ASSERT(r, SkIsAlign2(subset.fLeft) && SkIsAlign2(subset.fTop));
        } else {
            // No subsets will work.
            REPORTER_ASSERT(r, result == SkCodec::kUnimplemented);
        }
    }

    // SkScaledCodec tests
    if ((supportsScanlineDecoding || supportsSubsetDecoding) && supports_scaled_codec(path)) {

        SkAutoTDelete<SkStream> stream(resource(path));
        if (!stream) {
            SkDebugf("Missing resource '%s'\n", path);
            return;
        }

        SkAutoTDelete<SkCodec> codec(nullptr);
        if (isIncomplete) {
            size_t size = stream->getLength();
            SkAutoTUnref<SkData> data((SkData::NewFromStream(stream, 2 * size / 3)));
            codec.reset(SkScaledCodec::NewFromData(data));
        } else {
            codec.reset(SkScaledCodec::NewFromStream(stream.detach()));
        }
        if (!codec) {
            ERRORF(r, "Unable to decode '%s'", path);
            return;
        }

        SkBitmap bm;
        SkMD5::Digest scaledCodecDigest;
        test_codec(r, codec, bm, info, size, supports565, expectedResult, &scaledCodecDigest,
                &codecDigest);
    }

    // If we've just tested incomplete decodes, let's run the same test again on full decodes.
    if (isIncomplete) {
        check(r, path, size, supportsScanlineDecoding, supportsSubsetDecoding, supports565, false);
    }
}
// static
bool SkBitmapScaler::Resize(SkBitmap* resultPtr,
                            const SkBitmap& source,
                            ResizeMethod method,
                            int destWidth, int destHeight,
                            const SkIRect& destSubset,
                            const SkConvolutionProcs& convolveProcs,
                            SkBitmap::Allocator* allocator) {
  // 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)));

    SkIRect dest = { 0, 0, destWidth, destHeight };
    if (!dest.contains(destSubset)) {
        SkErrorInternals::SetError( kInvalidArgument_SkError,
                                    "Sorry, you passed me a bitmap resize "
                                    " method I have never heard of: %d",
                                    method );
    }

    // 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.config() != SkBitmap::kARGB_8888_Config) {
        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.setConfig(SkBitmap::kARGB_8888_Config,
                     destSubset.width(), destSubset.height(), 0,
                     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(NULL != resultPtr->getPixels());
    return true;
}
Esempio n. 26
0
// Basic test of the SkSpecialImage public API (e.g., peekTexture, peekPixels & draw)
static void test_image(const sk_sp<SkSpecialImage>& img, skiatest::Reporter* reporter,
                       GrContext* context, bool isGPUBacked,
                       int offset, int size) {
    const SkIRect subset = img->subset();
    REPORTER_ASSERT(reporter, offset == subset.left());
    REPORTER_ASSERT(reporter, offset == subset.top());
    REPORTER_ASSERT(reporter, kSmallerSize == subset.width());
    REPORTER_ASSERT(reporter, kSmallerSize == subset.height());

    //--------------
    // Test that isTextureBacked reports the correct backing type
    REPORTER_ASSERT(reporter, isGPUBacked == img->isTextureBacked());

#if SK_SUPPORT_GPU
    //--------------
    // Test asTextureProxyRef - as long as there is a context this should succeed
    if (context) {
        sk_sp<GrTextureProxy> proxy(img->asTextureProxyRef(context));
        REPORTER_ASSERT(reporter, proxy);
    }
#endif

    //--------------
    // Test getROPixels - this should always succeed regardless of backing store
    SkBitmap bitmap;
    REPORTER_ASSERT(reporter, img->getROPixels(&bitmap));
    if (context) {
        REPORTER_ASSERT(reporter, kSmallerSize == bitmap.width());
        REPORTER_ASSERT(reporter, kSmallerSize == bitmap.height());
    } else {
        REPORTER_ASSERT(reporter, size == bitmap.width());
        REPORTER_ASSERT(reporter, size == bitmap.height());
    }

    //--------------
    // Test that draw restricts itself to the subset
    SkImageFilter::OutputProperties outProps(img->getColorSpace());
    sk_sp<SkSpecialSurface> surf(img->makeSurface(outProps, SkISize::Make(kFullSize, kFullSize),
                                                  kPremul_SkAlphaType));

    SkCanvas* canvas = surf->getCanvas();

    canvas->clear(SK_ColorBLUE);
    img->draw(canvas, SkIntToScalar(kPad), SkIntToScalar(kPad), nullptr);

    SkBitmap bm;
    bm.allocN32Pixels(kFullSize, kFullSize, false);

    bool result = canvas->readPixels(bm.info(), bm.getPixels(), bm.rowBytes(), 0, 0);
    SkASSERT_RELEASE(result);

    // Only the center (red) portion should've been drawn into the canvas
    REPORTER_ASSERT(reporter, SK_ColorBLUE == bm.getColor(kPad-1, kPad-1));
    REPORTER_ASSERT(reporter, SK_ColorRED  == bm.getColor(kPad, kPad));
    REPORTER_ASSERT(reporter, SK_ColorRED  == bm.getColor(kSmallerSize+kPad-1,
                                                          kSmallerSize+kPad-1));
    REPORTER_ASSERT(reporter, SK_ColorBLUE == bm.getColor(kSmallerSize+kPad,
                                                          kSmallerSize+kPad));

    //--------------
    // Test that asImage & makeTightSurface return appropriately sized objects
    // of the correct backing type
    SkIRect newSubset = SkIRect::MakeWH(subset.width(), subset.height());
    {
        sk_sp<SkImage> tightImg(img->asImage(&newSubset));

        REPORTER_ASSERT(reporter, tightImg->width() == subset.width());
        REPORTER_ASSERT(reporter, tightImg->height() == subset.height());
        REPORTER_ASSERT(reporter, isGPUBacked == tightImg->isTextureBacked());
        SkPixmap tmpPixmap;
        REPORTER_ASSERT(reporter, isGPUBacked != !!tightImg->peekPixels(&tmpPixmap));
    }
    {
        SkImageFilter::OutputProperties outProps(img->getColorSpace());
        sk_sp<SkSurface> tightSurf(img->makeTightSurface(outProps, subset.size()));

        REPORTER_ASSERT(reporter, tightSurf->width() == subset.width());
        REPORTER_ASSERT(reporter, tightSurf->height() == subset.height());
        REPORTER_ASSERT(reporter, isGPUBacked ==
                     !!tightSurf->getTextureHandle(SkSurface::kDiscardWrite_BackendHandleAccess));
        SkPixmap tmpPixmap;
        REPORTER_ASSERT(reporter, isGPUBacked != !!tightSurf->peekPixels(&tmpPixmap));
    }
}
Esempio n. 27
0
static inline void blitrect(SkBlitter* blitter, const SkIRect& r) {
    blitter->blitRect(r.fLeft, r.fTop, r.width(), r.height());
}
Esempio n. 28
0
bool SkDisplacementMapEffect::filterImageGPU(Proxy* proxy, const SkBitmap& src, const Context& ctx,
                                             SkBitmap* result, SkIPoint* offset) const {
    SkBitmap colorBM = src;
    SkIPoint colorOffset = SkIPoint::Make(0, 0);
    if (!this->filterInputGPU(1, proxy, src, ctx, &colorBM, &colorOffset)) {
        return false;
    }
    SkBitmap displacementBM = src;
    SkIPoint displacementOffset = SkIPoint::Make(0, 0);
    if (!this->filterInputGPU(0, proxy, src, ctx, &displacementBM, &displacementOffset)) {
        return false;
    }
    SkIRect bounds;
    // Since GrDisplacementMapEffect does bounds checking on color pixel access, we don't need to
    // pad the color bitmap to bounds here.
    if (!this->applyCropRect(ctx, colorBM, colorOffset, &bounds)) {
        return false;
    }
    SkIRect displBounds;
    if (!this->applyCropRect(ctx, proxy, displacementBM,
                             &displacementOffset, &displBounds, &displacementBM)) {
        return false;
    }
    if (!bounds.intersect(displBounds)) {
        return false;
    }
    GrTexture* color = colorBM.getTexture();
    GrTexture* displacement = displacementBM.getTexture();
    GrContext* context = color->getContext();

    GrSurfaceDesc desc;
    desc.fFlags = kRenderTarget_GrSurfaceFlag;
    desc.fWidth = bounds.width();
    desc.fHeight = bounds.height();
    desc.fConfig = kSkia8888_GrPixelConfig;

    SkAutoTUnref<GrTexture> dst(context->textureProvider()->createApproxTexture(desc));

    if (!dst) {
        return false;
    }

    SkVector scale = SkVector::Make(fScale, fScale);
    ctx.ctm().mapVectors(&scale, 1);

    GrPaint paint;
    SkMatrix offsetMatrix = GrCoordTransform::MakeDivByTextureWHMatrix(displacement);
    offsetMatrix.preTranslate(SkIntToScalar(colorOffset.fX - displacementOffset.fX),
                              SkIntToScalar(colorOffset.fY - displacementOffset.fY));

    paint.addColorFragmentProcessor(
        GrDisplacementMapEffect::Create(fXChannelSelector,
                                        fYChannelSelector,
                                        scale,
                                        displacement,
                                        offsetMatrix,
                                        color,
                                        colorBM.dimensions()))->unref();
    paint.setPorterDuffXPFactory(SkXfermode::kSrc_Mode);
    SkIRect colorBounds = bounds;
    colorBounds.offset(-colorOffset);
    SkMatrix matrix;
    matrix.setTranslate(-SkIntToScalar(colorBounds.x()),
                        -SkIntToScalar(colorBounds.y()));

    SkAutoTUnref<GrDrawContext> drawContext(context->drawContext(dst->asRenderTarget()));
    if (!drawContext) {
        return false;
    }

    drawContext->drawRect(GrClip::WideOpen(), paint, matrix, SkRect::Make(colorBounds));
    offset->fX = bounds.left();
    offset->fY = bounds.top();
    GrWrapTextureInBitmap(dst, bounds.width(), bounds.height(), false, result);
    return true;
}
Esempio n. 29
0
// Create the layer information for the hoisted layer and secure the
// required texture/render target resources.
static void prepare_for_hoisting(GrLayerCache* layerCache,
                                 const SkPicture* topLevelPicture,
                                 const SkMatrix& initialMat,
                                 const SkLayerInfo::BlockInfo& info,
                                 const SkIRect& srcIR,
                                 const SkIRect& dstIR,
                                 SkTDArray<GrHoistedLayer>* needRendering,
                                 SkTDArray<GrHoistedLayer>* recycled,
                                 bool attemptToAtlas,
                                 int numSamples) {
    const SkPicture* pict = info.fPicture ? info.fPicture : topLevelPicture;

    GrCachedLayer* layer = layerCache->findLayerOrCreate(topLevelPicture->uniqueID(),
                                                         SkToInt(info.fSaveLayerOpID),
                                                         SkToInt(info.fRestoreOpID),
                                                         srcIR,
                                                         dstIR,
                                                         initialMat,
                                                         info.fKey,
                                                         info.fKeySize,
                                                         info.fPaint);
    GrSurfaceDesc desc;
    desc.fFlags = kRenderTarget_GrSurfaceFlag;
    desc.fWidth = srcIR.width();
    desc.fHeight = srcIR.height();
    desc.fConfig = kSkia8888_GrPixelConfig;
    desc.fSampleCnt = numSamples;

    bool locked, needsRendering;
    if (attemptToAtlas) {
        locked = layerCache->tryToAtlas(layer, desc, &needsRendering);
    } else {
        locked = layerCache->lock(layer, desc, &needsRendering);
    }
    if (!locked) {
        // GPU resources could not be secured for the hoisting of this layer
        return;
    }

    if (attemptToAtlas) {
        SkASSERT(layer->isAtlased());
    }

    GrHoistedLayer* hl;

    if (needsRendering) {
        if (!attemptToAtlas) {
            SkASSERT(!layer->isAtlased());
        }
        hl = needRendering->append();
    } else {
        hl = recycled->append();
    }

    layerCache->addUse(layer);
    hl->fLayer = layer;
    hl->fPicture = pict;
    hl->fLocalMat = info.fLocalMat;
    hl->fInitialMat = initialMat;
    hl->fPreMat = initialMat;
    hl->fPreMat.preConcat(info.fPreMat);
}
bool SkMorphologyImageFilter::filterImageGeneric(SkMorphologyImageFilter::Proc procX,
                                                 SkMorphologyImageFilter::Proc procY,
                                                 Proxy* proxy,
                                                 const SkBitmap& source,
                                                 const Context& ctx,
                                                 SkBitmap* dst,
                                                 SkIPoint* offset) const {
    SkBitmap src = source;
    SkIPoint srcOffset = SkIPoint::Make(0, 0);
    if (getInput(0) && !getInput(0)->filterImage(proxy, source, ctx, &src, &srcOffset)) {
        return false;
    }

    if (src.colorType() != kN32_SkColorType) {
        return false;
    }

    SkIRect bounds;
    if (!this->applyCropRect(ctx, proxy, src, &srcOffset, &bounds, &src)) {
        return false;
    }

    SkAutoLockPixels alp(src);
    if (!src.getPixels()) {
        return false;
    }

    if (!dst->allocPixels(src.info().makeWH(bounds.width(), bounds.height()))) {
        return false;
    }

    SkVector radius = SkVector::Make(SkIntToScalar(this->radius().width()),
                                     SkIntToScalar(this->radius().height()));
    ctx.ctm().mapVectors(&radius, 1);
    int width = SkScalarFloorToInt(radius.fX);
    int height = SkScalarFloorToInt(radius.fY);

    if (width < 0 || height < 0) {
        return false;
    }

    SkIRect srcBounds = bounds;
    srcBounds.offset(-srcOffset);

    if (width == 0 && height == 0) {
        src.extractSubset(dst, srcBounds);
        offset->fX = bounds.left();
        offset->fY = bounds.top();
        return true;
    }

    SkBitmap temp;
    if (!temp.allocPixels(dst->info())) {
        return false;
    }

    if (width > 0 && height > 0) {
        callProcX(procX, src, &temp, width, srcBounds);
        SkIRect tmpBounds = SkIRect::MakeWH(srcBounds.width(), srcBounds.height());
        callProcY(procY, temp, dst, height, tmpBounds);
    } else if (width > 0) {
        callProcX(procX, src, dst, width, srcBounds);
    } else if (height > 0) {
        callProcY(procY, src, dst, height, srcBounds);
    }
    offset->fX = bounds.left();
    offset->fY = bounds.top();
    return true;
}