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(); }
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()); }
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()); }
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)); }
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))); } }
// 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; } }
/* 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()); } }
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); } } } }
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; }
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); } }
// 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; }
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; }
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; }
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); } }
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; }
/* * 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; }
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; }
// 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; }