void GrGLMatrixConvolutionEffect::onSetData(const GrGLSLProgramDataManager& pdman,
                                            const GrProcessor& processor) {
    const GrMatrixConvolutionEffect& conv = processor.cast<GrMatrixConvolutionEffect>();
    GrTexture& texture = *conv.texture(0);
    // the code we generated was for a specific kernel size
    SkASSERT(conv.kernelSize() == fKernelSize);
    float imageIncrement[2];
    float ySign = texture.origin() == kTopLeft_GrSurfaceOrigin ? 1.0f : -1.0f;
    imageIncrement[0] = 1.0f / texture.width();
    imageIncrement[1] = ySign / texture.height();
    pdman.set2fv(fImageIncrementUni, 1, imageIncrement);
    pdman.set2fv(fKernelOffsetUni, 1, conv.kernelOffset());
    pdman.set1fv(fKernelUni, fKernelSize.width() * fKernelSize.height(), conv.kernel());
    pdman.set1f(fGainUni, conv.gain());
    pdman.set1f(fBiasUni, conv.bias());
    fDomain.setData(pdman, conv.domain(), texture.origin());
}
// Creates an SkImage that is backed by SkDiscardablePixelRef.
PassRefPtr<SkImage> DeferredImageDecoder::createImage(size_t index, bool knownToBeOpaque) const
{
    SkISize decodedSize = m_frameGenerator->getFullSize();
    ASSERT(decodedSize.width() > 0);
    ASSERT(decodedSize.height() > 0);

    const SkImageInfo info = SkImageInfo::MakeN32(decodedSize.width(), decodedSize.height(),
        knownToBeOpaque ? kOpaque_SkAlphaType : kPremul_SkAlphaType);

    DecodingImageGenerator* generator = new DecodingImageGenerator(m_frameGenerator, info, index);
    RefPtr<SkImage> image = adoptRef(SkImage::NewFromGenerator(generator));
    if (!image)
        return nullptr;

    generator->setGenerationId(image->uniqueID());
    return image.release();
}
Example #3
0
SkSVGDevice::SkSVGDevice(const SkISize& size, SkXMLWriter* writer)
    : fWriter(writer)
    , fResourceBucket(SkNEW(ResourceBucket)) {
    SkASSERT(writer);

    fLegacyBitmap.setInfo(SkImageInfo::MakeUnknown(size.width(), size.height()));

    fWriter->writeHeader();

    // The root <svg> tag gets closed by the destructor.
    fRootElement.reset(SkNEW_ARGS(AutoElement, ("svg", fWriter)));

    fRootElement->addAttribute("xmlns", "http://www.w3.org/2000/svg");
    fRootElement->addAttribute("xmlns:xlink", "http://www.w3.org/1999/xlink");
    fRootElement->addAttribute("width", size.width());
    fRootElement->addAttribute("height", size.height());
}
Example #4
0
SkSVGDevice::SkSVGDevice(const SkISize& size, SkXMLWriter* writer)
    : INHERITED(SkImageInfo::MakeUnknown(size.fWidth, size.fHeight),
                SkSurfaceProps(0, kUnknown_SkPixelGeometry))
    , fWriter(writer)
    , fResourceBucket(new ResourceBucket)
{
    SkASSERT(writer);

    fWriter->writeHeader();

    // The root <svg> tag gets closed by the destructor.
    fRootElement.reset(new AutoElement("svg", fWriter));

    fRootElement->addAttribute("xmlns", "http://www.w3.org/2000/svg");
    fRootElement->addAttribute("xmlns:xlink", "http://www.w3.org/1999/xlink");
    fRootElement->addAttribute("width", size.width());
    fRootElement->addAttribute("height", size.height());
}
Example #5
0
File: image.cpp Project: DXGL/skia
static void drawJpeg(SkCanvas* canvas, const SkISize& size) {
    // TODO: Make this draw a file that is checked in, so it can
    // be exercised on machines other than mike's. Will require a
    // rebaseline.
    SkAutoDataUnref data(SkData::NewFromFileName("/Users/mike/Downloads/skia.google.jpeg"));
    if (NULL == data.get()) {
        return;
    }
    SkImage* image = SkImage::NewFromGenerator(
                SkDecodingImageGenerator::Create(data, SkDecodingImageGenerator::Options()));
    if (image) {
        SkAutoCanvasRestore acr(canvas, true);
        canvas->scale(size.width() * 1.0f / image->width(),
                      size.height() * 1.0f / image->height());
        canvas->drawImage(image, 0, 0, NULL);
        image->unref();
    }
}
// Static function to create a 2D convolution
std::unique_ptr<GrFragmentProcessor> GrMatrixConvolutionEffect::MakeGaussian(
        sk_sp<GrTextureProxy> proxy,
        const SkIRect& bounds,
        const SkISize& kernelSize,
        SkScalar gain,
        SkScalar bias,
        const SkIPoint& kernelOffset,
        GrTextureDomain::Mode tileMode,
        bool convolveAlpha,
        SkScalar sigmaX,
        SkScalar sigmaY) {
    float kernel[MAX_KERNEL_SIZE];

    fill_in_2D_gaussian_kernel(kernel, kernelSize.width(), kernelSize.height(), sigmaX, sigmaY);

    return std::unique_ptr<GrFragmentProcessor>(
            new GrMatrixConvolutionEffect(std::move(proxy), bounds, kernelSize, kernel, gain, bias,
                                          kernelOffset, tileMode, convolveAlpha));
}
Example #7
0
void TileGridTask::draw() {
    const SkTileGridPicture::TileGridInfo info = {
        fTileSize,
        SkISize::Make(0,0),   // Overlap between adjacent tiles.
        SkIPoint::Make(0,0),  // Offset.
    };
    const SkISize size = fGM->getISize();
    SkTileGridPicture recorded(size.width(), size.height(), info);
    RecordPicture(fGM.get(), &recorded, SkPicture::kUsePathBoundsForClip_RecordingFlag);

    SkBitmap full;
    SetupBitmap(fReference.colorType(), fGM.get(), &full);
    SkCanvas fullCanvas(full);

    SkBitmap tile;
    tile.allocPixels(SkImageInfo::Make(fTileSize.width(), fTileSize.height(),
                                       fReference.colorType(), kPremul_SkAlphaType));
    SkCanvas tileCanvas(tile);

    SkPaint paint;
    paint.setXfermodeMode(SkXfermode::kSrc_Mode);

    for (int y = 0; y < tiles_needed(full.height(), tile.height()); y++) {
        for (int x = 0; x < tiles_needed(full.width(), tile.width()); x++) {
            SkAutoCanvasRestore ar(&tileCanvas, true/*also save now*/);

            const SkScalar xOffset = SkIntToScalar(x * tile.width()),
                           yOffset = SkIntToScalar(y * tile.height());
            SkMatrix matrix = tileCanvas.getTotalMatrix();
            matrix.postTranslate(-xOffset, -yOffset);
            tileCanvas.setMatrix(matrix);

            recorded.draw(&tileCanvas);
            tileCanvas.flush();
            fullCanvas.drawBitmap(tile, xOffset, yOffset, &paint);
        }
    }

    if (!BitmapsEqual(full, fReference)) {
        this->fail();
        this->spawnChild(SkNEW_ARGS(WriteTask, (*this, full)));
    }
}
Example #8
0
// calculates sampleSize in x and y direction
void SkScaledCodec::ComputeSampleSize(const SkISize& dstDim, const SkISize& srcDim,
                                      int* sampleXPtr, int* sampleYPtr) {
    int srcWidth = srcDim.width();
    int dstWidth = dstDim.width();
    int srcHeight = srcDim.height();
    int dstHeight = dstDim.height();

    int sampleX = srcWidth / dstWidth;
    int sampleY = srcHeight / dstHeight;

    // only support down sampling, not up sampling
    SkASSERT(dstWidth <= srcWidth);
    SkASSERT(dstHeight <= srcHeight);

    // sampleX and sampleY should be equal unless the original sampleSize requested was
    // larger than srcWidth or srcHeight.
    // If so, the result of this is dstWidth or dstHeight = 1. This functionality
    // allows for tall thin images to still be scaled down by scaling factors.

    if (sampleX != sampleY){
        if (1 != dstWidth && 1 != dstHeight) {

            // rounding during onGetScaledDimensions can cause different sampleSizes
            // Ex: srcWidth = 79, srcHeight = 20, sampleSize = 10
            // dstWidth = 7, dstHeight = 2, sampleX = 79/7 = 11, sampleY = 20/2 = 10
            // correct for this rounding by comparing width to sampleY and height to sampleX

            if (get_scaled_dimension(srcWidth, sampleY) == dstWidth) {
                sampleX = sampleY;
            } else if (get_scaled_dimension(srcHeight, sampleX) == dstHeight) {
                sampleY = sampleX;
            }
        }
    }

    if (sampleXPtr) {
        *sampleXPtr = sampleX;
    }
    if (sampleYPtr) {
        *sampleYPtr = sampleY;
    }
}
Example #9
0
/*
If you execute skp_parser with one argument, it spits out a json representation
of the skp, but that's incomplete since it's missing many binary blobs (these
could represent images or typefaces or just anything that doesn't currently
have a json representation).  Each unique blob is labeled with a string in the
form "data/%d".  So for example:

    tools/git-sync-deps
    bin/gn gen out/debug
    ninja -C out/debug dm skp_parser
    out/debug/dm -m grayscale -w /tmp/dm --config skp
    out/debug/skp_parser /tmp/dm/skp/gm/grayscalejpg.skp | less
    out/debug/skp_parser /tmp/dm/skp/gm/grayscalejpg.skp | grep data
    out/debug/skp_parser /tmp/dm/skp/gm/grayscalejpg.skp data/0 | file -
    out/debug/skp_parser /tmp/dm/skp/gm/grayscalejpg.skp data/0 > /tmp/data0.png

"data/0" is an image that the SKP serializer has encoded as PNG.
*/
int main(int argc, char** argv) {
    if (argc < 2) {
        SkDebugf("Usage:\n  %s SKP_FILE [DATA_URL]\n", argv[0]);
        return 1;
    }
    SkFILEStream input(argv[1]);
    if (!input.isValid()) {
        SkDebugf("Bad file: '%s'\n", argv[1]);
        return 2;
    }
    sk_sp<SkPicture> pic = SkPicture::MakeFromStream(&input);
    if (!pic) {
        SkDebugf("Bad skp: '%s'\n", argv[1]);
        return 3;
    }
    SkISize size = pic->cullRect().roundOut().size();
    SkDebugCanvas debugCanvas(size.width(), size.height());
    pic->playback(&debugCanvas);
    std::unique_ptr<SkCanvas> nullCanvas = SkMakeNullCanvas();
    UrlDataManager dataManager(SkString("data"));
    Json::Value json = debugCanvas.toJSON(
            dataManager, debugCanvas.getSize(), nullCanvas.get());
    if (argc > 2) {
        if (UrlDataManager::UrlData* data =
            dataManager.getDataFromUrl(SkString(argv[2]))) {
            SkData* skdata = data->fData.get();
            SkASSERT(skdata);
            #ifdef SK_BUILD_FOR_WIN
            fflush(stdout);
            (void)_setmode(_fileno(stdout), _O_BINARY);
            #endif
            fwrite(skdata->data(), skdata->size(), 1, stdout);
        } else {
            SkDebugf("Bad data url.\n");
            return 4;
        }
    } else {
        Json::StyledStreamWriter("  ").write(std::cout, json);
    }
    return 0;
}
// Creates a SkBitmap that is backed by SkDiscardablePixelRef.
SkBitmap DeferredImageDecoder::createBitmap(size_t index)
{
    SkISize decodedSize = m_frameGenerator->getFullSize();
    ASSERT(decodedSize.width() > 0);
    ASSERT(decodedSize.height() > 0);

#if SK_B32_SHIFT // Little-endian RGBA pixels. (Android)
    const SkColorType colorType = kRGBA_8888_SkColorType;
#else
    const SkColorType colorType = kBGRA_8888_SkColorType;
#endif
    const SkImageInfo info = SkImageInfo::Make(decodedSize.width(), decodedSize.height(), colorType, kPremul_SkAlphaType);

    SkBitmap bitmap;
    DecodingImageGenerator* generator = new DecodingImageGenerator(m_frameGenerator, info, index);
    bool installed = SkInstallDiscardablePixelRef(generator, &bitmap);
    ASSERT_UNUSED(installed, installed);
    bitmap.pixelRef()->setURI(labelDiscardable);
    generator->setGenerationId(bitmap.getGenerationID());
    return bitmap;
}
    void drawTheImage(SkCanvas* canvas, const SkISize& size, SkFilterQuality filter,
                      SkScalar dx, SkScalar dy) {
        SkPaint paint;
        paint.setAntiAlias(true);
        paint.setFilterQuality(filter);

        SkAutoCanvasRestore acr(canvas, true);

        canvas->translate(dx, dy);

        canvas->translate(SkScalarHalf(size.width()), SkScalarHalf(size.height()));
        canvas->scale(fScale, fScale);
        canvas->rotate(fAngle);
        canvas->drawImage(fImage, -SkScalarHalf(fImage->width()), -SkScalarHalf(fImage->height()),
                          &paint);

        if (false) {
            acr.restore();
            draw_box_frame(canvas, size.width(), size.height());
        }
    }
Example #12
0
void Viewer::setupCurrentSlide(int previousSlide) {
    if (fCurrentSlide == previousSlide) {
        return; // no change; do nothing
    }

    fGesture.reset();
    fDefaultMatrix.reset();
    fDefaultMatrixInv.reset();

    if (fWindow->supportsContentRect() && fWindow->scaleContentToFit()) {
        const SkRect contentRect = fWindow->getContentRect();
        const SkISize slideSize = fSlides[fCurrentSlide]->getDimensions();
        const SkRect slideBounds = SkRect::MakeIWH(slideSize.width(), slideSize.height());
        if (contentRect.width() > 0 && contentRect.height() > 0) {
            fDefaultMatrix.setRectToRect(slideBounds, contentRect, SkMatrix::kStart_ScaleToFit);
            SkAssertResult(fDefaultMatrix.invert(&fDefaultMatrixInv));
        }
    }

    if (fWindow->supportsContentRect()) {
        const SkISize slideSize = fSlides[fCurrentSlide]->getDimensions();
        SkRect windowRect = fWindow->getContentRect();
        fDefaultMatrixInv.mapRect(&windowRect);
        fGesture.setTransLimit(SkRect::MakeWH(slideSize.width(), slideSize.height()), windowRect);
    }

    this->updateTitle();
    this->updateUIState();
    fSlides[fCurrentSlide]->load();
    if (previousSlide >= 0) {
        fSlides[previousSlide]->unload();
    }
    fWindow->inval();
}
    void onDraw(int loops, SkCanvas* canvas) override {
        canvas->clear(SK_ColorBLACK);

        SkISize size = canvas->getDeviceSize();

        int offX = (size.width() - kWindowSize) / kNumStepsX;
        int offY = (size.height() - kWindowSize) / kNumStepsY;

        SkPaint paint;

        paint.setColor(SK_ColorBLUE);

        canvas->drawCircle(SkIntToScalar(size.width()/2),
                           SkIntToScalar(size.height()/2),
                           SkIntToScalar(size.width()/2),
                           paint);

        SkBitmap bitmap;

        bitmap.setInfo(SkImageInfo::MakeN32Premul(kWindowSize, kWindowSize));

        for (int i = 0; i < loops; i++) {
            for (int x = 0; x < kNumStepsX; ++x) {
                for (int y = 0; y < kNumStepsY; ++y) {
                    canvas->readPixels(&bitmap, x * offX, y * offY);
                }
            }
        }
    }
Example #14
0
static SkISize best_scaled_dimensions(const SkISize& origDims, const SkISize& nativeDims,
                                      const SkISize& scaledCodecDims, float desiredScale) {
    if (nativeDims == scaledCodecDims) {
        // does not matter which to return if equal. Return here to skip below calculations
        return nativeDims;
    }
    float idealWidth = origDims.width() * desiredScale;
    float idealHeight = origDims.height() * desiredScale;

    // calculate difference between native dimensions and ideal dimensions
    float nativeWDiff = SkTAbs(idealWidth - nativeDims.width());
    float nativeHDiff = SkTAbs(idealHeight - nativeDims.height());
    float nativeDiff = nativeWDiff + nativeHDiff;

    // Native scaling is preferred to sampling.  If we can scale natively to
    // within one of the ideal value, we should choose to scale natively.
    if (nativeWDiff < 1.0f && nativeHDiff < 1.0f) {
        return nativeDims;
    }

    // calculate difference between scaledCodec dimensions and ideal dimensions
    float scaledCodecWDiff = SkTAbs(idealWidth - scaledCodecDims.width());
    float scaledCodecHDiff = SkTAbs(idealHeight - scaledCodecDims.height());
    float scaledCodecDiff = scaledCodecWDiff + scaledCodecHDiff;

    // return dimensions closest to ideal dimensions.
    // If the differences are equal, return nativeDims, as native scaling is more efficient.
    return nativeDiff > scaledCodecDiff ? scaledCodecDims : nativeDims;
}
Example #15
0
void GMSampleView::onDrawContent(SkCanvas* canvas) {
    SkPictureRecorder recorder;
    SkCanvas* origCanvas = canvas;

    if (false) {
        SkISize size = fGM->getISize();
        canvas = recorder.beginRecording(SkRect::MakeIWH(size.width(), size.height()));
    }

    {
        SkAutoCanvasRestore acr(canvas, fShowSize);
        fGM->drawContent(canvas);
    }

    if (origCanvas != canvas) {
        sk_sp<SkPicture> pic = recorder.finishRecordingAsPicture();
        if (false) {
            pic = round_trip_serialize(pic.get());
        }
        origCanvas->drawPicture(pic);
        canvas = origCanvas;
    }

    if (fShowSize) {
        SkISize size = fGM->getISize();
        SkRect r = SkRect::MakeWH(SkIntToScalar(size.width()),
                                  SkIntToScalar(size.height()));
        SkPaint paint;
        paint.setColor(0x40FF8833);
        canvas->drawRect(r, paint);
    }
}
Example #16
0
 // Only called once. Could be part of the constructor.
 void stitch() {
     SkScalar tileWidth  = SkIntToScalar(fTileSize.width());
     SkScalar tileHeight = SkIntToScalar(fTileSize.height());
     SkASSERT(tileWidth > 0 && tileHeight > 0);
     // When stitching tiled turbulence, the frequencies must be adjusted
     // so that the tile borders will be continuous.
     if (fBaseFrequency.fX) {
         SkScalar lowFrequencx =
             SkScalarFloorToScalar(tileWidth * fBaseFrequency.fX) / tileWidth;
         SkScalar highFrequencx =
             SkScalarCeilToScalar(tileWidth * fBaseFrequency.fX) / tileWidth;
         // BaseFrequency should be non-negative according to the standard.
         if (SkScalarDiv(fBaseFrequency.fX, lowFrequencx) <
             SkScalarDiv(highFrequencx, fBaseFrequency.fX)) {
             fBaseFrequency.fX = lowFrequencx;
         } else {
             fBaseFrequency.fX = highFrequencx;
         }
     }
     if (fBaseFrequency.fY) {
         SkScalar lowFrequency =
             SkScalarFloorToScalar(tileHeight * fBaseFrequency.fY) / tileHeight;
         SkScalar highFrequency =
             SkScalarCeilToScalar(tileHeight * fBaseFrequency.fY) / tileHeight;
         if (SkScalarDiv(fBaseFrequency.fY, lowFrequency) <
             SkScalarDiv(highFrequency, fBaseFrequency.fY)) {
             fBaseFrequency.fY = lowFrequency;
         } else {
             fBaseFrequency.fY = highFrequency;
         }
     }
     // Set up TurbulenceInitial stitch values.
     fStitchDataInit.fWidth  =
         SkScalarRoundToInt(tileWidth * fBaseFrequency.fX);
     fStitchDataInit.fWrapX  = kPerlinNoise + fStitchDataInit.fWidth;
     fStitchDataInit.fHeight =
         SkScalarRoundToInt(tileHeight * fBaseFrequency.fY);
     fStitchDataInit.fWrapY  = kPerlinNoise + fStitchDataInit.fHeight;
 }
GrMatrixConvolutionEffect::GrMatrixConvolutionEffect(GrTexture* texture,
                                                     const SkIRect& bounds,
                                                     const SkISize& kernelSize,
                                                     const SkScalar* kernel,
                                                     SkScalar gain,
                                                     SkScalar bias,
                                                     const SkIPoint& kernelOffset,
                                                     GrTextureDomain::Mode tileMode,
                                                     bool convolveAlpha)
  : INHERITED(texture, GrCoordTransform::MakeDivByTextureWHMatrix(texture)),
    fKernelSize(kernelSize),
    fGain(SkScalarToFloat(gain)),
    fBias(SkScalarToFloat(bias) / 255.0f),
    fConvolveAlpha(convolveAlpha),
    fDomain(GrTextureDomain::MakeTexelDomainForMode(texture, bounds, tileMode), tileMode) {
    this->initClassID<GrMatrixConvolutionEffect>();
    for (int i = 0; i < kernelSize.width() * kernelSize.height(); i++) {
        fKernel[i] = SkScalarToFloat(kernel[i]);
    }
    fKernelOffset[0] = static_cast<float>(kernelOffset.x());
    fKernelOffset[1] = static_cast<float>(kernelOffset.y());
}
bool SkPictureImageGenerator::onGenerateScaledPixels(const SkISize& scaledSize,
                                                     const SkIPoint& scaledOrigin,
                                                     const SkPixmap& scaledPixels) {
    int w = scaledSize.width();
    int h = scaledSize.height();

    const SkScalar scaleX = SkIntToScalar(w) / this->getInfo().width();
    const SkScalar scaleY = SkIntToScalar(h) / this->getInfo().height();
    SkMatrix matrix = SkMatrix::MakeScale(scaleX, scaleY);
    matrix.postTranslate(-SkIntToScalar(scaledOrigin.x()), -SkIntToScalar(scaledOrigin.y()));

    SkBitmap bitmap;
    if (!bitmap.installPixels(scaledPixels)) {
        return false;
    }

    bitmap.eraseColor(SK_ColorTRANSPARENT);
    SkCanvas canvas(bitmap, SkSurfaceProps(0, kUnknown_SkPixelGeometry));
    matrix.preConcat(fMatrix);
    canvas.drawPicture(fPicture, &matrix, fPaint.getMaybeNull());
    return true;
}
Example #19
0
void GrVkPipelineState::setRenderTargetState(const GrRenderTarget* rt, GrSurfaceOrigin origin) {

    // Load the RT height uniform if it is needed to y-flip gl_FragCoord.
    if (fBuiltinUniformHandles.fRTHeightUni.isValid() &&
        fRenderTargetState.fRenderTargetSize.fHeight != rt->height()) {
        fDataManager.set1f(fBuiltinUniformHandles.fRTHeightUni, SkIntToScalar(rt->height()));
    }

    // set RT adjustment
    SkISize size;
    size.set(rt->width(), rt->height());
    SkASSERT(fBuiltinUniformHandles.fRTAdjustmentUni.isValid());
    if (fRenderTargetState.fRenderTargetOrigin != origin ||
        fRenderTargetState.fRenderTargetSize != size) {
        fRenderTargetState.fRenderTargetSize = size;
        fRenderTargetState.fRenderTargetOrigin = origin;

        float rtAdjustmentVec[4];
        fRenderTargetState.getRTAdjustmentVec(rtAdjustmentVec);
        fDataManager.set4fv(fBuiltinUniformHandles.fRTAdjustmentUni, 1, rtAdjustmentVec);
    }
}
SkBitmap DeferredImageDecoder::createLazyDecodingBitmap(size_t index)
{
    SkISize fullSize = SkISize::Make(m_actualDecoder->size().width(), m_actualDecoder->size().height());
    ASSERT(!fullSize.isEmpty());

    SkIRect fullRect = SkIRect::MakeSize(fullSize);

    // Creates a lazily decoded SkPixelRef that references the entire image without scaling.
    SkBitmap bitmap;
    bitmap.setConfig(SkBitmap::kARGB_8888_Config, fullSize.width(), fullSize.height());
    bitmap.setPixelRef(new LazyDecodingPixelRef(m_frameGenerator, fullSize, index, fullRect))->unref();

    // Use the URI to identify this as a lazily decoded SkPixelRef of type LazyDecodingPixelRef.
    // FIXME: It would be more useful to give the actual image URI.
    bitmap.pixelRef()->setURI(labelLazyDecoded);

    // Inform the bitmap that we will never change the pixels. This is a performance hint
    // subsystems that may try to cache this bitmap (e.g. pictures, pipes, gpu, pdf, etc.)
    bitmap.setImmutable();

    return bitmap;
}
Example #21
0
void SkPatchGrid::draw(SkCanvas* canvas, SkPaint& paint) {
    int* maxCols = new int[fCols];
    int* maxRows = new int[fRows];
    memset(maxCols, 0, fCols * sizeof(int));
    memset(maxRows, 0, fRows * sizeof(int));
    
    // Get the maximum level of detail per axis for each row and column
    for (int y = 0; y < fRows; y++) {
        for (int x = 0; x < fCols; x++) {
            SkPoint cubics[12];
            this->getPatch(x, y, cubics, NULL, NULL);
            SkMatrix matrix = canvas->getTotalMatrix();
            SkISize lod = SkPatchUtils::GetLevelOfDetail(cubics, &matrix);
            maxCols[x] = SkMax32(maxCols[x], lod.width());
            maxRows[y] = SkMax32(maxRows[y], lod.height());
        }
    }
    // Draw the patches by generating their geometry with the maximum level of detail per axis.
    for (int x = 0; x < fCols; x++) {
        for (int y = 0; y < fRows; y++) {
            SkPoint cubics[12];
            SkPoint texCoords[4];
            SkColor colors[4];
            this->getPatch(x, y, cubics, colors, texCoords);
            SkPatchUtils::VertexData data;
            if (SkPatchUtils::getVertexData(&data, cubics,
                                            fModeFlags & kColors_VertexType ? colors : NULL,
                                            fModeFlags & kTexs_VertexType ? texCoords : NULL,
                                            maxCols[x], maxRows[y])) {
                canvas->drawVertices(SkCanvas::kTriangles_VertexMode, data.fVertexCount,
                                     data.fPoints, data.fTexCoords, data.fColors, fXferMode,
                                     data.fIndices, data.fIndexCount, paint);
            }
        }
    }
    delete[] maxCols;
    delete[] maxRows;
}
Example #22
0
void GrGLProgram::onSetMatrixAndRenderTargetHeight(GrGpu::DrawType drawType,
                                                   const GrOptDrawState& optState) {
    const GrRenderTarget* rt = optState.getRenderTarget();
    SkISize size;
    size.set(rt->width(), rt->height());
    if (fMatrixState.fRenderTargetOrigin != rt->origin() ||
        fMatrixState.fRenderTargetSize != size ||
        !fMatrixState.fViewMatrix.cheapEqualTo(optState.getViewMatrix())) {
        SkASSERT(fBuiltinUniformHandles.fViewMatrixUni.isValid());

        fMatrixState.fViewMatrix = optState.getViewMatrix();
        fMatrixState.fRenderTargetSize = size;
        fMatrixState.fRenderTargetOrigin = rt->origin();

        GrGLfloat viewMatrix[3 * 3];
        fMatrixState.getGLMatrix<3>(viewMatrix);
        fProgramDataManager.setMatrix3f(fBuiltinUniformHandles.fViewMatrixUni, viewMatrix);

        GrGLfloat rtAdjustmentVec[4];
        fMatrixState.getRTAdjustmentVec(rtAdjustmentVec);
        fProgramDataManager.set4fv(fBuiltinUniformHandles.fRTAdjustmentUni, 1, rtAdjustmentVec);
    }
}
Example #23
0
 void test(SkCanvas* canvas, int x, int y, SkPerlinNoiseShader::Type type,
           float baseFrequencyX, float baseFrequencyY, int numOctaves, float seed,
           bool stitchTiles) {
     SkISize tileSize = SkISize::Make(fSize.width() / 2, fSize.height() / 2);
     SkShader* shader = (type == SkPerlinNoiseShader::kFractalNoise_Type) ?
         SkPerlinNoiseShader::CreateFractalNoise(baseFrequencyX, baseFrequencyY, numOctaves,
                                                seed, stitchTiles ? &tileSize : nullptr) :
         SkPerlinNoiseShader::CreateTurbulence(baseFrequencyX, baseFrequencyY, numOctaves,
                                             seed, stitchTiles ? &tileSize : nullptr);
     SkPaint paint;
     paint.setShader(shader)->unref();
     if (stitchTiles) {
         drawRect(canvas, x, y, paint, tileSize);
         x += tileSize.width();
         drawRect(canvas, x, y, paint, tileSize);
         y += tileSize.width();
         drawRect(canvas, x, y, paint, tileSize);
         x -= tileSize.width();
         drawRect(canvas, x, y, paint, tileSize);
     } else {
         drawRect(canvas, x, y, paint, fSize);
     }
 }
bool ImageFrameGenerator::decodeAndScale(const SkImageInfo& info, size_t index, void* pixels, size_t rowBytes)
{
    // This method is called to populate a discardable memory owned by Skia.

    // Prevents concurrent decode or scale operations on the same image data.
    MutexLocker lock(m_decodeMutex);

    // This implementation does not support scaling so check the requested size.
    SkISize scaledSize = SkISize::Make(info.width(), info.height());
    ASSERT(m_fullSize == scaledSize);

    if (m_decodeFailedAndEmpty)
        return false;

    TRACE_EVENT2("blink", "ImageFrameGenerator::decodeAndScale", "generator", this, "decodeCount", m_decodeCount);

    m_externalAllocator = adoptPtr(new ExternalMemoryAllocator(info, pixels, rowBytes));

    SkBitmap bitmap = tryToResumeDecode(scaledSize, index);
    if (bitmap.isNull())
        return false;

    // Don't keep the allocator because it contains a pointer to memory
    // that we do not own.
    m_externalAllocator.clear();

    ASSERT(bitmap.width() == scaledSize.width());
    ASSERT(bitmap.height() == scaledSize.height());

    bool result = true;
    SkAutoLockPixels bitmapLock(bitmap);
    // Check to see if decoder has written directly to the memory provided
    // by Skia. If not make a copy.
    if (bitmap.getPixels() != pixels)
        result = bitmap.copyPixelsTo(pixels, rowBytes * info.height(), rowBytes);
    return result;
}
sk_sp<SkAnimatedImage> SkAnimatedImage::Make(std::unique_ptr<SkAndroidCodec> codec,
        SkISize scaledSize, SkIRect cropRect, sk_sp<SkPicture> postProcess) {
    if (!codec) {
        return nullptr;
    }

    SkISize decodeSize = scaledSize;
    auto decodeInfo = codec->getInfo();
    if (codec->getEncodedFormat() == SkEncodedImageFormat::kWEBP
            && scaledSize.width()  < decodeInfo.width()
            && scaledSize.height() < decodeInfo.height()) {
        // libwebp can decode to arbitrary smaller sizes.
        decodeInfo = decodeInfo.makeWH(decodeSize.width(), decodeSize.height());
    }

    auto image = sk_sp<SkAnimatedImage>(new SkAnimatedImage(std::move(codec), scaledSize,
                decodeInfo, cropRect, std::move(postProcess)));
    if (!image->fDisplayFrame.fBitmap.getPixels()) {
        // tryAllocPixels failed.
        return nullptr;
    }

    return image;
}
Example #26
0
/*
 * Checks if we can natively scale to the requested dimensions and natively scales the
 * dimensions if possible
 */
bool SkJpegCodec::onDimensionsSupported(const SkISize& size) {
    if (setjmp(fDecoderMgr->getJmpBuf())) {
        return fDecoderMgr->returnFalse("onDimensionsSupported/setjmp");
    }

    const unsigned int dstWidth = size.width();
    const unsigned int dstHeight = size.height();

    // Set up a fake decompress struct in order to use libjpeg to calculate output dimensions
    // FIXME: Why is this necessary?
    jpeg_decompress_struct dinfo;
    sk_bzero(&dinfo, sizeof(dinfo));
    dinfo.image_width = this->getInfo().width();
    dinfo.image_height = this->getInfo().height();
    dinfo.global_state = fReadyState;

    // libjpeg-turbo can scale to 1/8, 1/4, 3/8, 1/2, 5/8, 3/4, 7/8, and 1/1
    unsigned int num = 8;
    const unsigned int denom = 8;
    calc_output_dimensions(&dinfo, num, denom);
    while (dinfo.output_width != dstWidth || dinfo.output_height != dstHeight) {

        // Return a failure if we have tried all of the possible scales
        if (1 == num || dstWidth > dinfo.output_width || dstHeight > dinfo.output_height) {
            return false;
        }

        // Try the next scale
        num -= 1;
        calc_output_dimensions(&dinfo, num, denom);
    }

    fDecoderMgr->dinfo()->scale_num = num;
    fDecoderMgr->dinfo()->scale_denom = denom;
    return true;
}
Example #27
0
SkShader* SkPictureShader::refBitmapShader(const SkMatrix& matrix, const SkMatrix* localM) const {
    SkASSERT(fPicture && fPicture->width() > 0 && fPicture->height() > 0);

    SkMatrix m;
    m.setConcat(matrix, this->getLocalMatrix());
    if (localM) {
        m.preConcat(*localM);
    }

    // Use a rotation-invariant scale
    SkPoint scale;
    if (!SkDecomposeUpper2x2(m, NULL, &scale, NULL)) {
        // Decomposition failed, use an approximation.
        scale.set(SkScalarSqrt(m.getScaleX() * m.getScaleX() + m.getSkewX() * m.getSkewX()),
                  SkScalarSqrt(m.getScaleY() * m.getScaleY() + m.getSkewY() * m.getSkewY()));
    }
    SkSize scaledSize = SkSize::Make(scale.x() * fPicture->width(), scale.y() * fPicture->height());

    SkISize tileSize = scaledSize.toRound();
    if (tileSize.isEmpty()) {
        return NULL;
    }

    // The actual scale, compensating for rounding.
    SkSize tileScale = SkSize::Make(SkIntToScalar(tileSize.width()) / fPicture->width(),
                                    SkIntToScalar(tileSize.height()) / fPicture->height());

    SkAutoMutexAcquire ama(fCachedBitmapShaderMutex);

    if (!fCachedBitmapShader || tileScale != fCachedTileScale) {
        SkBitmap bm;
        if (!bm.allocN32Pixels(tileSize.width(), tileSize.height())) {
            return NULL;
        }
        bm.eraseColor(SK_ColorTRANSPARENT);

        SkCanvas canvas(bm);
        canvas.scale(tileScale.width(), tileScale.height());
        canvas.drawPicture(fPicture);

        fCachedTileScale = tileScale;

        SkMatrix shaderMatrix = this->getLocalMatrix();
        shaderMatrix.preScale(1 / tileScale.width(), 1 / tileScale.height());
        fCachedBitmapShader.reset(CreateBitmapShader(bm, fTmx, fTmy, &shaderMatrix));
    }

    // Increment the ref counter inside the mutex to ensure the returned pointer is still valid.
    // Otherwise, the pointer may have been overwritten on a different thread before the object's
    // ref count was incremented.
    fCachedBitmapShader.get()->ref();
    return fCachedBitmapShader;
}
bool NativeImageSkia::shouldCacheResampling(const SkISize& scaledImageSize, const SkIRect& scaledImageSubset) const
{
    // Check whether the requested dimensions match previous request.
    bool matchesPreviousRequest = m_cachedImageInfo.isEqual(scaledImageSize, scaledImageSubset);
    if (matchesPreviousRequest)
        ++m_resizeRequests;
    else {
        m_cachedImageInfo.set(scaledImageSize, scaledImageSubset);
        m_resizeRequests = 0;
        // Reset m_resizedImage now, because we don't distinguish
        // between the last requested resize info and m_resizedImage's
        // resize info.
        m_resizedImage.reset();
    }

    // We can not cache incomplete frames. This might be a good optimization in
    // the future, were we know how much of the frame has been decoded, so when
    // we incrementally draw more of the image, we only have to resample the
    // parts that are changed.
    if (!isDataComplete())
        return false;

    // If the destination bitmap is excessively large, we'll never allow caching.
    static const unsigned long long kLargeBitmapSize = 4096ULL * 4096ULL;
    unsigned long long fullSize = static_cast<unsigned long long>(scaledImageSize.width()) * static_cast<unsigned long long>(scaledImageSize.height());
    unsigned long long fragmentSize = static_cast<unsigned long long>(scaledImageSubset.width()) * static_cast<unsigned long long>(scaledImageSubset.height());

    if (fragmentSize > kLargeBitmapSize)
        return false;

    // If the destination bitmap is small, we'll always allow caching, since
    // there is not very much penalty for computing it and it may come in handy.
    static const unsigned kSmallBitmapSize = 4096;
    if (fragmentSize <= kSmallBitmapSize)
        return true;

    // If "too many" requests have been made for this bitmap, we assume that
    // many more will be made as well, and we'll go ahead and cache it.
    static const int kManyRequestThreshold = 4;
    if (m_resizeRequests >= kManyRequestThreshold)
        return true;

    // If more than 1/4 of the resized image is requested, it's worth caching.
    return fragmentSize > fullSize / 4;
}
Example #29
0
// This function is used to scale an image and extract a scaled fragment.
//
// ALGORITHM
//
// Because the scaled image size has to be integers, we approximate the real
// scale with the following formula (only X direction is shown):
//
// scaledImageWidth = round(scaleX * imageRect.width())
// approximateScaleX = scaledImageWidth / imageRect.width()
//
// With this method we maintain a constant scale factor among fragments in
// the scaled image. This allows fragments to stitch together to form the
// full scaled image. The downside is there will be a small difference
// between |scaleX| and |approximateScaleX|.
//
// A scaled image fragment is identified by:
//
// - Scaled image size
// - Scaled image fragment rectangle (IntRect)
//
// Scaled image size has been determined and the next step is to compute the
// rectangle for the scaled image fragment which needs to be an IntRect.
//
// scaledSrcRect = srcRect * (approximateScaleX, approximateScaleY)
// enclosingScaledSrcRect = enclosingIntRect(scaledSrcRect)
//
// Finally we extract the scaled image fragment using
// (scaledImageSize, enclosingScaledSrcRect).
//
static SkBitmap extractScaledImageFragment(const NativeImageSkia& bitmap, const SkRect& srcRect, float scaleX, float scaleY, SkRect* scaledSrcRect, SkIRect* enclosingScaledSrcRect)
{
    SkISize imageSize = SkISize::Make(bitmap.bitmap().width(), bitmap.bitmap().height());
    SkISize scaledImageSize = SkISize::Make(clampToInteger(roundf(imageSize.width() * scaleX)),
        clampToInteger(roundf(imageSize.height() * scaleY)));

    SkRect imageRect = SkRect::MakeWH(imageSize.width(), imageSize.height());
    SkRect scaledImageRect = SkRect::MakeWH(scaledImageSize.width(), scaledImageSize.height());

    SkMatrix scaleTransform;
    scaleTransform.setRectToRect(imageRect, scaledImageRect, SkMatrix::kFill_ScaleToFit);
    scaleTransform.mapRect(scaledSrcRect, srcRect);

    scaledSrcRect->intersect(scaledImageRect);
    *enclosingScaledSrcRect = enclosingIntRect(*scaledSrcRect);

    // |enclosingScaledSrcRect| can be larger than |scaledImageSize| because
    // of float inaccuracy so clip to get inside.
    enclosingScaledSrcRect->intersect(SkIRect::MakeSize(scaledImageSize));
    return bitmap.resizedBitmap(scaledImageSize, *enclosingScaledSrcRect);
}
SkBitmap DeferredImageDecoder::createResizedLazyDecodingBitmap(const SkBitmap& bitmap, const SkISize& scaledSize, const SkIRect& scaledSubset)
{
    LazyDecodingPixelRef* pixelRef = static_cast<LazyDecodingPixelRef*>(bitmap.pixelRef());

    int rowBytes = 0;
    rowBytes = SkBitmap::ComputeRowBytes(SkBitmap::kARGB_8888_Config, scaledSize.width());

    SkBitmap resizedBitmap;
    resizedBitmap.setConfig(SkBitmap::kARGB_8888_Config, scaledSubset.width(), scaledSubset.height(), rowBytes);

    // FIXME: This code has the potential problem that multiple
    // LazyDecodingPixelRefs are created even though they share the same
    // scaled size and ImageFrameGenerator.
    resizedBitmap.setPixelRef(new LazyDecodingPixelRef(pixelRef->frameGenerator(), scaledSize, pixelRef->frameIndex(), scaledSubset))->unref();

    // See comments in createLazyDecodingBitmap().
    resizedBitmap.setImmutable();
    return resizedBitmap;
}