// check if scaling to dstInfo size from srcInfo size using sampleSize is possible static bool scaling_supported(const SkISize& dstDim, const SkISize& srcDim, int* sampleX, int* sampleY) { SkScaledCodec::ComputeSampleSize(dstDim, srcDim, sampleX, sampleY); const int dstWidth = dstDim.width(); const int dstHeight = dstDim.height(); const int srcWidth = srcDim.width(); const int srcHeight = srcDim.height(); // only support down sampling, not up sampling if (dstWidth > srcWidth || dstHeight > srcHeight) { return false; } // check that srcWidth is scaled down by an integer value if (get_scaled_dimension(srcWidth, *sampleX) != dstWidth) { return false; } // check that src height is scaled down by an integer value if (get_scaled_dimension(srcHeight, *sampleY) != dstHeight) { return false; } // 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) { return false; } } return true; }
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); } } } }
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); } }
// 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). // SkBitmap NativeImageSkia::extractScaledImageFragment(const SkRect& srcRect, float scaleX, float scaleY, SkRect* scaledSrcRect) const { SkISize imageSize = SkISize::Make(bitmap().width(), 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); bool ok = scaledSrcRect->intersect(scaledImageRect); ASSERT_UNUSED(ok, ok); SkIRect enclosingScaledSrcRect = enclosingIntRect(*scaledSrcRect); // |enclosingScaledSrcRect| can be larger than |scaledImageSize| because // of float inaccuracy so clip to get inside. ok = enclosingScaledSrcRect.intersect(SkIRect::MakeSize(scaledImageSize)); ASSERT_UNUSED(ok, ok); // scaledSrcRect is relative to the pixel snapped fragment we're extracting. scaledSrcRect->offset(-enclosingScaledSrcRect.x(), -enclosingScaledSrcRect.y()); return resizedBitmap(scaledImageSize, enclosingScaledSrcRect); }
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; }
static void test_dimensions(skiatest::Reporter* r, const char path[]) { // Create the codec from the resource file SkAutoTDelete<SkStream> stream(resource(path)); if (!stream) { SkDebugf("Missing resource '%s'\n", path); return; } SkAutoTDelete<SkCodec> codec(SkCodec::NewFromStream(stream.detach())); if (!codec) { ERRORF(r, "Unable to create codec '%s'", path); return; } // Check that the decode is successful for a variety of scales for (float scale = -0.05f; scale < 2.0f; scale += 0.05f) { // Scale the output dimensions SkISize scaledDims = codec->getScaledDimensions(scale); SkImageInfo scaledInfo = codec->getInfo().makeWH(scaledDims.width(), scaledDims.height()); // Set up for the decode size_t rowBytes = scaledDims.width() * sizeof(SkPMColor); size_t totalBytes = scaledInfo.getSafeSize(rowBytes); SkAutoTMalloc<SkPMColor> pixels(totalBytes); SkImageGenerator::Result result = codec->getPixels(scaledInfo, pixels.get(), rowBytes, NULL, NULL, NULL); REPORTER_ASSERT(r, SkImageGenerator::kSuccess == result); } }
static void test_dimensions(skiatest::Reporter* r, const char path[]) { // Create the codec from the resource file SkAutoTDelete<SkStream> stream(resource(path)); if (!stream) { SkDebugf("Missing resource '%s'\n", path); return; } SkAutoTDelete<SkAndroidCodec> codec(SkAndroidCodec::NewFromStream(stream.detach())); if (!codec) { ERRORF(r, "Unable to create codec '%s'", path); return; } // Check that the decode is successful for a variety of scales for (int sampleSize = 1; sampleSize < 32; sampleSize++) { // Scale the output dimensions SkISize scaledDims = codec->getSampledDimensions(sampleSize); SkImageInfo scaledInfo = codec->getInfo() .makeWH(scaledDims.width(), scaledDims.height()) .makeColorType(kN32_SkColorType); // Set up for the decode size_t rowBytes = scaledDims.width() * sizeof(SkPMColor); size_t totalBytes = scaledInfo.getSafeSize(rowBytes); SkAutoTMalloc<SkPMColor> pixels(totalBytes); SkAndroidCodec::AndroidOptions options; options.fSampleSize = sampleSize; SkCodec::Result result = codec->getAndroidPixels(scaledInfo, pixels.get(), rowBytes, &options); REPORTER_ASSERT(r, SkCodec::kSuccess == result); } }
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 drawClippedRect(SkCanvas* canvas, int x, int y, const SkPaint& paint) { canvas->save(); canvas->clipRect(SkRect::MakeXYWH(SkIntToScalar(x), SkIntToScalar(y), SkIntToScalar(fSize.width()), SkIntToScalar(fSize.height()))); SkRect r = SkRect::MakeXYWH(SkIntToScalar(x), SkIntToScalar(y), SkIntToScalar(fSize.width()), SkIntToScalar(fSize.height())); canvas->drawRect(r, paint); canvas->restore(); }
void make_bitmap() { fBitmap.allocN32Pixels(fSize.width(), fSize.height()); SkCanvas canvas(fBitmap); canvas.clear(0x00000000); SkPaint paint; paint.setAntiAlias(true); SkShader* shader = MakeLinear(fSize); paint.setShader(shader); SkRect r = { 0, 0, SkIntToScalar(fSize.width()), SkIntToScalar(fSize.height()) }; canvas.drawRect(r, paint); shader->unref(); }
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; }
GrMatrixConvolutionEffect::GrMatrixConvolutionEffect(sk_sp<GrTextureProxy> proxy, const SkIRect& bounds, const SkISize& kernelSize, const SkScalar* kernel, SkScalar gain, SkScalar bias, const SkIPoint& kernelOffset, GrTextureDomain::Mode tileMode, bool convolveAlpha) // To advertise either the modulation or opaqueness optimizations we'd have to examine the // parameters. : INHERITED(kGrMatrixConvolutionEffect_ClassID, kNone_OptimizationFlags) , fCoordTransform(proxy.get()) , fDomain(proxy.get(), GrTextureDomain::MakeTexelDomainForMode(bounds, tileMode), tileMode) , fTextureSampler(std::move(proxy)) , fKernelSize(kernelSize) , fGain(SkScalarToFloat(gain)) , fBias(SkScalarToFloat(bias) / 255.0f) , fConvolveAlpha(convolveAlpha) { this->addCoordTransform(&fCoordTransform); this->addTextureSampler(&fTextureSampler); 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()); }
SkBitmap DeferredImageDecoder::createLazyDecodingBitmap() { 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()); m_frameGenerator = ImageFrameGenerator::create(fullSize, m_data.release(), m_allDataReceived); m_actualDecoder.clear(); bitmap.setPixelRef(new LazyDecodingPixelRef(m_frameGenerator, fullSize, 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(); // FIXME: Setting bitmap.setIsOpaque() is big performance gain if possible. We can // do so safely if the image is fully loaded and it is a JPEG image, or if the image was // decoded before. return bitmap; }
std::unique_ptr<GrFragmentProcessor> GrMatrixConvolutionEffect::TestCreate(GrProcessorTestData* d) { int texIdx = d->fRandom->nextBool() ? GrProcessorUnitTest::kSkiaPMTextureIdx : GrProcessorUnitTest::kAlphaTextureIdx; sk_sp<GrTextureProxy> proxy = d->textureProxy(texIdx); int width = d->fRandom->nextRangeU(1, MAX_KERNEL_SIZE); int height = d->fRandom->nextRangeU(1, MAX_KERNEL_SIZE / width); SkISize kernelSize = SkISize::Make(width, height); std::unique_ptr<SkScalar[]> kernel(new SkScalar[width * height]); for (int i = 0; i < width * height; i++) { kernel.get()[i] = d->fRandom->nextSScalar1(); } SkScalar gain = d->fRandom->nextSScalar1(); SkScalar bias = d->fRandom->nextSScalar1(); SkIPoint kernelOffset = SkIPoint::Make(d->fRandom->nextRangeU(0, kernelSize.width()), d->fRandom->nextRangeU(0, kernelSize.height())); SkIRect bounds = SkIRect::MakeXYWH(d->fRandom->nextRangeU(0, proxy->width()), d->fRandom->nextRangeU(0, proxy->height()), d->fRandom->nextRangeU(0, proxy->width()), d->fRandom->nextRangeU(0, proxy->height())); GrTextureDomain::Mode tileMode = static_cast<GrTextureDomain::Mode>(d->fRandom->nextRangeU(0, 2)); bool convolveAlpha = d->fRandom->nextBool(); return GrMatrixConvolutionEffect::Make(std::move(proxy), bounds, kernelSize, kernel.get(), gain, bias, kernelOffset, tileMode, convolveAlpha); }
bool SkRasterClip::op(const SkPath& path, const SkISize& size, SkRegion::Op op, bool doAA) { // base is used to limit the size (and therefore memory allocation) of the // region that results from scan converting devPath. SkRegion base; if (SkRegion::kIntersect_Op == op) { // since we are intersect, we can do better (tighter) with currRgn's // bounds, than just using the device. However, if currRgn is complex, // our region blitter may hork, so we do that case in two steps. if (this->isRect()) { // FIXME: we should also be able to do this when this->isBW(), // but relaxing the test above triggers GM asserts in // SkRgnBuilder::blitH(). We need to investigate what's going on. return this->setPath(path, this->bwRgn(), doAA); } else { base.setRect(this->getBounds()); SkRasterClip clip; clip.setPath(path, base, doAA); return this->op(clip, op); } } else { base.setRect(0, 0, size.width(), size.height()); if (SkRegion::kReplace_Op == op) { return this->setPath(path, base, doAA); } else { SkRasterClip clip; clip.setPath(path, base, doAA); return this->op(clip, op); } } }
PassOwnPtr<ScaledImageFragment> createCompleteImage(const SkISize& size) { SkBitmap bitmap; bitmap.setConfig(SkBitmap::kARGB_8888_Config, size.width(), size.height()); bitmap.allocPixels(); return ScaledImageFragment::createComplete(size, 0, bitmap); }
bool ImageFrameGenerator::decodeAndScale(size_t index, const SkImageInfo& info, void* pixels, size_t rowBytes) { // Prevent concurrent decode or scale operations on the same image data. MutexLocker lock(m_decodeMutex); if (m_decodeFailed) return false; TRACE_EVENT1("blink", "ImageFrameGenerator::decodeAndScale", "frame index", static_cast<int>(index)); m_externalAllocator = adoptPtr(new ExternalMemoryAllocator(info, pixels, rowBytes)); // This implementation does not support scaling so check the requested size. SkISize scaledSize = SkISize::Make(info.width(), info.height()); ASSERT(m_fullSize == scaledSize); SkBitmap bitmap = tryToResumeDecode(index, scaledSize); 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(); // Check to see if the decoder has written directly to the pixel memory // provided. If not, make a copy. ASSERT(bitmap.width() == scaledSize.width()); ASSERT(bitmap.height() == scaledSize.height()); SkAutoLockPixels bitmapLock(bitmap); if (bitmap.getPixels() != pixels) return bitmap.copyPixelsTo(pixels, rowBytes * info.height(), rowBytes); return true; }
/** * Write the canvas to the specified path. * @param canvas Must be non-null. Canvas to be written to a file. * @param path Path for the file to be written. Should have no extension; write() will append * an appropriate one. Passed in by value so it can be modified. * @param jsonSummaryPtr If not null, add image results to this summary. * @return bool True if the Canvas is written to a file. * * TODO(epoger): Right now, all canvases must pass through this function in order to be appended * to the ImageResultsSummary. We need some way to add bitmaps to the ImageResultsSummary * even if --writePath has not been specified (and thus this function is not called). * * One fix would be to pass in these path elements separately, and allow this function to be * called even if --writePath was not specified... * const char *outputDir // NULL if we don't want to write image files to disk * const char *filename // name we use within JSON summary, and as the filename within outputDir */ static bool write(SkCanvas* canvas, const SkString* path, ImageResultsSummary *jsonSummaryPtr) { SkASSERT(canvas != NULL); if (NULL == canvas) { return false; } SkASSERT(path != NULL); // TODO(epoger): we want to remove this constraint, as noted above SkString fullPathname(*path); fullPathname.append(".png"); SkBitmap bitmap; SkISize size = canvas->getDeviceSize(); sk_tools::setup_bitmap(&bitmap, size.width(), size.height()); canvas->readPixels(&bitmap, 0, 0); sk_tools::force_all_opaque(bitmap); if (NULL != jsonSummaryPtr) { // EPOGER: This is a hacky way of constructing the filename associated with the // image checksum; we assume that outputDir is not NULL, and we remove outputDir // from fullPathname. // // EPOGER: what about including the config type within hashFilename? That way, // we could combine results of different config types without conflicting filenames. SkString hashFilename; sk_tools::get_basename(&hashFilename, fullPathname); jsonSummaryPtr->add(hashFilename.c_str(), bitmap); } return SkImageEncoder::EncodeFile(fullPathname.c_str(), bitmap, SkImageEncoder::kPNG_Type, 100); }
bool Canvas2DLayerBridge::restoreSurface() { ASSERT(!m_destructionInProgress); if (m_destructionInProgress) return false; ASSERT(m_layer && !m_isSurfaceValid); WebGraphicsContext3D* sharedContext = 0; m_layer->clearTexture(); m_contextProvider = adoptPtr(Platform::current()->createSharedOffscreenGraphicsContext3DProvider()); if (m_contextProvider) sharedContext = m_contextProvider->context3d(); if (sharedContext && !sharedContext->isContextLost()) { SkISize skSize = m_canvas->getBaseLayerSize(); IntSize size(skSize.width(), skSize.height()); RefPtr<SkSurface> surface(createSkSurface(m_contextProvider->grContext(), size, m_msaaSampleCount, m_opacityMode)); if (surface.get()) { m_surface = surface.release(); m_canvas->setSurface(m_surface.get()); m_isSurfaceValid = true; // FIXME: draw sad canvas picture into new buffer crbug.com/243842 } } return m_isSurfaceValid; }
sk_sp<SkImageFilter> SkMatrixConvolutionImageFilter::Make(const SkISize& kernelSize, const SkScalar* kernel, SkScalar gain, SkScalar bias, const SkIPoint& kernelOffset, TileMode tileMode, bool convolveAlpha, sk_sp<SkImageFilter> input, const CropRect* cropRect) { if (kernelSize.width() < 1 || kernelSize.height() < 1) { return nullptr; } if (gMaxKernelSize / kernelSize.fWidth < kernelSize.fHeight) { return nullptr; } if (!kernel) { return nullptr; } if ((kernelOffset.fX < 0) || (kernelOffset.fX >= kernelSize.fWidth) || (kernelOffset.fY < 0) || (kernelOffset.fY >= kernelSize.fHeight)) { return nullptr; } return sk_sp<SkImageFilter>(new SkMatrixConvolutionImageFilter(kernelSize, kernel, gain, bias, kernelOffset, tileMode, convolveAlpha, std::move(input), cropRect)); }
/* * Read enough of the stream to initialize the SkGifCodec. * Returns a bool representing success or failure. * * @param codecOut * If it returned true, and codecOut was not nullptr, * codecOut will be set to a new SkGifCodec. * * @param gifOut * If it returned true, and codecOut was nullptr, * gifOut must be non-nullptr and gifOut will be set to a new * GifFileType pointer. * * @param stream * Deleted on failure. * codecOut will take ownership of it in the case where we created a codec. * Ownership is unchanged when we returned a gifOut. * */ bool SkGifCodec::ReadHeader(SkStream* stream, SkCodec** codecOut, GifFileType** gifOut) { SkAutoTDelete<SkStream> streamDeleter(stream); // Read gif header, logical screen descriptor, and global color table SkAutoTCallVProc<GifFileType, CloseGif> gif(open_gif(stream)); if (nullptr == gif) { gif_error("DGifOpen failed.\n"); return false; } // Read through gif extensions to get to the image data. Set the // transparent index based on the extension data. uint32_t transIndex; SkCodec::Result result = ReadUpToFirstImage(gif, &transIndex); if (kSuccess != result){ return false; } // Read the image descriptor if (GIF_ERROR == DGifGetImageDesc(gif)) { return false; } // If reading the image descriptor is successful, the image count will be // incremented. SkASSERT(gif->ImageCount >= 1); if (nullptr != codecOut) { SkISize size; SkIRect frameRect; if (!GetDimensions(gif, &size, &frameRect)) { gif_error("Invalid gif size.\n"); return false; } bool frameIsSubset = (size != frameRect.size()); // Determine the encoded alpha type. The transIndex might be valid if it less // than 256. We are not certain that the index is valid until we process the color // table, since some gifs have color tables with less than 256 colors. If // there might be a valid transparent index, we must indicate that the image has // alpha. // In the case where we must support alpha, we indicate kBinary, since every // pixel will either be fully opaque or fully transparent. SkEncodedInfo::Alpha alpha = (transIndex < 256) ? SkEncodedInfo::kBinary_Alpha : SkEncodedInfo::kOpaque_Alpha; // Return the codec // Use kPalette since Gifs are encoded with a color table. // Use 8-bits per component, since this is the output we get from giflib. // FIXME: Gifs can actually be encoded with 4-bits per pixel. Can we support this? SkEncodedInfo info = SkEncodedInfo::Make(SkEncodedInfo::kPalette_Color, alpha, 8); *codecOut = new SkGifCodec(size.width(), size.height(), info, streamDeleter.release(), gif.release(), transIndex, frameRect, frameIsSubset); } else { SkASSERT(nullptr != gifOut); streamDeleter.release(); *gifOut = gif.release(); } return true; }
SkFlattenable* SkMatrixConvolutionImageFilter::CreateProc(SkReadBuffer& buffer) { SK_IMAGEFILTER_UNFLATTEN_COMMON(common, 1); SkISize kernelSize; kernelSize.fWidth = buffer.readInt(); kernelSize.fHeight = buffer.readInt(); const int count = buffer.getArrayCount(); const int64_t kernelArea = sk_64_mul(kernelSize.width(), kernelSize.height()); if (!buffer.validate(kernelArea == count)) { return nullptr; } SkAutoSTArray<16, SkScalar> kernel(count); if (!buffer.readScalarArray(kernel.get(), count)) { return nullptr; } SkScalar gain = buffer.readScalar(); SkScalar bias = buffer.readScalar(); SkIPoint kernelOffset; kernelOffset.fX = buffer.readInt(); kernelOffset.fY = buffer.readInt(); TileMode tileMode = (TileMode)buffer.readInt(); bool convolveAlpha = buffer.readBool(); return Create(kernelSize, kernel.get(), gain, bias, kernelOffset, tileMode, convolveAlpha, common.getInput(0), &common.cropRect()); }
// unused static void TestNWayCanvasStateConsistency( skiatest::Reporter* reporter, const TestData& d, CanvasTestStep* testStep, const SkCanvas& referenceCanvas) { SkBitmap indirectStore1; createBitmap(&indirectStore1, 0xFFFFFFFF); SkCanvas indirectCanvas1(indirectStore1); SkBitmap indirectStore2; createBitmap(&indirectStore2, 0xFFFFFFFF); SkCanvas indirectCanvas2(indirectStore2); SkISize canvasSize = referenceCanvas.getDeviceSize(); SkNWayCanvas nWayCanvas(canvasSize.width(), canvasSize.height()); nWayCanvas.addCanvas(&indirectCanvas1); nWayCanvas.addCanvas(&indirectCanvas2); testStep->setAssertMessageFormat(kNWayDrawAssertMessageFormat); testStep->draw(&nWayCanvas, d, reporter); // Verify that the SkNWayCanvas reports consitent state testStep->setAssertMessageFormat(kNWayStateAssertMessageFormat); AssertCanvasStatesEqual(reporter, d, &nWayCanvas, &referenceCanvas, testStep); // Verify that the indirect canvases report consitent state testStep->setAssertMessageFormat(kNWayIndirect1StateAssertMessageFormat); AssertCanvasStatesEqual(reporter, d, &indirectCanvas1, &referenceCanvas, testStep); testStep->setAssertMessageFormat(kNWayIndirect2StateAssertMessageFormat); AssertCanvasStatesEqual(reporter, d, &indirectCanvas2, &referenceCanvas, testStep); }
static void test_mipmap_generation(int width, int height, int expectedMipLevelCount, skiatest::Reporter* reporter) { SkBitmap bm; bm.allocN32Pixels(width, height); bm.eraseColor(SK_ColorWHITE); SkAutoTUnref<SkMipMap> mm(SkMipMap::Build(bm, nullptr)); const int mipLevelCount = mm->countLevels(); REPORTER_ASSERT(reporter, mipLevelCount == expectedMipLevelCount); REPORTER_ASSERT(reporter, mipLevelCount == SkMipMap::ComputeLevelCount(width, height)); for (int i = 0; i < mipLevelCount; ++i) { SkMipMap::Level level; REPORTER_ASSERT(reporter, mm->getLevel(i, &level)); // Make sure the mipmaps contain valid data and that the sizes are correct REPORTER_ASSERT(reporter, level.fPixmap.addr()); SkISize size = SkMipMap::ComputeLevelSize(width, height, i); REPORTER_ASSERT(reporter, level.fPixmap.width() == size.width()); REPORTER_ASSERT(reporter, level.fPixmap.height() == size.height()); // + 1 because SkMipMap does not include the base mipmap level. int twoToTheMipLevel = 1 << (i + 1); int currentWidth = width / twoToTheMipLevel; int currentHeight = height / twoToTheMipLevel; REPORTER_ASSERT(reporter, level.fPixmap.width() == currentWidth); REPORTER_ASSERT(reporter, level.fPixmap.height() == currentHeight); } }
static bool write_bitmap(const char outName[], const SkBitmap& bm) { SkISize size = opaqueSize(bm); SkBitmap dst; setup_bitmap(&dst, size.width(), size.height()); for (int y = 0 ; y < dst.height(); y++) { for (int x = 0 ; x < dst.width(); x++) { SkColor color = bm.getColor(x, y); if (SkColorGetA(color) != 0xff) { int a = SkColorGetA(color); int r = SkColorGetR(color); int g = SkColorGetG(color); int b = SkColorGetB(color); if (a == 0) { r = g = b = 0; } else { r = (r * a) / 255; g = (g * a) / 255; b = (b * a) / 255; a = 255; } color = SkColorSetARGB((U8CPU)a, (U8CPU)r, (U8CPU)g, (U8CPU)b); } *dst.getAddr32(x, y) = color; } } return SkImageEncoder::EncodeFile(outName, dst, SkImageEncoder::kPNG_Type, 100); }
std::unique_ptr<SkImageGenerator> SkPictureImageGenerator::Make(const SkISize& size, sk_sp<SkPicture> picture, const SkMatrix* matrix, const SkPaint* paint, SkImage::BitDepth bitDepth, sk_sp<SkColorSpace> colorSpace) { if (!picture || size.isEmpty()) { return nullptr; } if (SkImage::BitDepth::kF16 == bitDepth && (!colorSpace || !colorSpace->gammaIsLinear())) { return nullptr; } if (colorSpace && (!colorSpace->gammaCloseToSRGB() && !colorSpace->gammaIsLinear())) { return nullptr; } SkColorType colorType = kN32_SkColorType; if (SkImage::BitDepth::kF16 == bitDepth) { colorType = kRGBA_F16_SkColorType; } SkImageInfo info = SkImageInfo::Make(size.width(), size.height(), colorType, kPremul_SkAlphaType, std::move(colorSpace)); return std::unique_ptr<SkImageGenerator>( new SkPictureImageGenerator(info, std::move(picture), matrix, paint)); }
static sk_sp<SkSpecialImage> apply_morphology( GrContext* context, SkSpecialImage* input, const SkIRect& rect, GrMorphologyEffect::Type morphType, SkISize radius, const SkImageFilter::OutputProperties& outputProperties) { sk_sp<GrTextureProxy> srcTexture(input->asTextureProxyRef(context)); SkASSERT(srcTexture); sk_sp<SkColorSpace> colorSpace = sk_ref_sp(outputProperties.colorSpace()); GrPixelConfig config = SkColorType2GrPixelConfig(outputProperties.colorType()); // setup new clip const GrFixedClip clip(SkIRect::MakeWH(srcTexture->width(), srcTexture->height())); const SkIRect dstRect = SkIRect::MakeWH(rect.width(), rect.height()); SkIRect srcRect = rect; SkASSERT(radius.width() > 0 || radius.height() > 0); if (radius.fWidth > 0) { sk_sp<GrRenderTargetContext> dstRTContext( context->contextPriv().makeDeferredRenderTargetContext( SkBackingFit::kApprox, rect.width(), rect.height(), config, colorSpace)); if (!dstRTContext) { return nullptr; } apply_morphology_pass(dstRTContext.get(), clip, std::move(srcTexture), srcRect, dstRect, radius.fWidth, morphType, GrMorphologyEffect::Direction::kX); SkIRect clearRect = SkIRect::MakeXYWH(dstRect.fLeft, dstRect.fBottom, dstRect.width(), radius.fHeight); GrColor clearColor = GrMorphologyEffect::Type::kErode == morphType ? SK_ColorWHITE : SK_ColorTRANSPARENT; dstRTContext->clear(&clearRect, clearColor, GrRenderTargetContext::CanClearFullscreen::kNo); srcTexture = dstRTContext->asTextureProxyRef(); srcRect = dstRect; } if (radius.fHeight > 0) { sk_sp<GrRenderTargetContext> dstRTContext( context->contextPriv().makeDeferredRenderTargetContext( SkBackingFit::kApprox, rect.width(), rect.height(), config, colorSpace)); if (!dstRTContext) { return nullptr; } apply_morphology_pass(dstRTContext.get(), clip, std::move(srcTexture), srcRect, dstRect, radius.fHeight, morphType, GrMorphologyEffect::Direction::kY); srcTexture = dstRTContext->asTextureProxyRef(); } return SkSpecialImage::MakeDeferredFromGpu(context, SkIRect::MakeWH(rect.width(), rect.height()), kNeedNewImageUniqueID_SpecialImage, std::move(srcTexture), std::move(colorSpace), &input->props()); }
bool GrAtlasManager::initAtlas(GrMaskFormat format) { int index = MaskFormatToAtlasIndex(format); if (fAtlases[index] == nullptr) { GrPixelConfig config = mask_format_to_pixel_config(format); SkISize atlasDimensions = fAtlasConfigs.atlasDimensions(format); SkISize numPlots = fAtlasConfigs.numPlots(format); fAtlases[index] = GrDrawOpAtlas::Make( fProxyProvider, config, atlasDimensions.width(), atlasDimensions.height(), numPlots.width(), numPlots.height(), fAllowMultitexturing, &GrGlyphCache::HandleEviction, fGlyphCache); if (!fAtlases[index]) { return false; } } return true; }
void drawRect(SkCanvas* canvas, int x, int y, const SkPaint& paint, const SkISize& size) { canvas->save(); canvas->translate(SkIntToScalar(x), SkIntToScalar(y)); SkRect r = SkRect::MakeWH(SkIntToScalar(size.width()), SkIntToScalar(size.height())); canvas->drawRect(r, paint); canvas->restore(); }
void GM::drawSizeBounds(SkCanvas* canvas, SkColor color) { SkISize size = this->getISize(); SkRect r = SkRect::MakeWH(SkIntToScalar(size.width()), SkIntToScalar(size.height())); SkPaint paint; paint.setColor(color); canvas->drawRect(r, paint); }