unsigned MaxComponentDifference(const SkBitmap& a, const SkBitmap& b) { if (a.info() != b.info()) { SkFAIL("Can't compare bitmaps of different shapes."); } unsigned max = 0; const SkAutoLockPixels lockA(a), lockB(b); if (a.info().colorType() == kRGB_565_SkColorType) { // 565 is special/annoying because its 3 components straddle 2 bytes. const uint16_t* aPixels = (const uint16_t*)a.getPixels(); const uint16_t* bPixels = (const uint16_t*)b.getPixels(); for (size_t i = 0; i < a.getSize() / 2; i++) { unsigned ar, ag, ab, br, bg, bb; unpack_565(aPixels[i], &ar, &ag, &ab); unpack_565(bPixels[i], &br, &bg, &bb); max = SkTMax(max, abs_diff(ar, br)); max = SkTMax(max, abs_diff(ag, bg)); max = SkTMax(max, abs_diff(ab, bb)); } } else { // Everything else we produce is byte aligned, so max component diff == max byte diff. const uint8_t* aBytes = (const uint8_t*)a.getPixels(); const uint8_t* bBytes = (const uint8_t*)b.getPixels(); for (size_t i = 0; i < a.getSize(); i++) { max = SkTMax(max, abs_diff(aBytes[i], bBytes[i])); } } return max; }
SkBitmapDevice::SkBitmapDevice(const SkBitmap& bitmap) : INHERITED(bitmap.info(), SkSurfaceProps(SkSurfaceProps::kLegacyFontHost_InitType)) , fBitmap(bitmap) { SkASSERT(valid_for_bitmap_device(bitmap.info(), nullptr)); fBitmap.lockPixels(); }
sk_sp<SkSpecialSurface> SkSpecialSurface::MakeFromBitmap(const SkIRect& subset, SkBitmap& bm, const SkSurfaceProps* props) { if (subset.isEmpty() || !SkSurfaceValidateRasterInfo(bm.info(), bm.rowBytes())) { return nullptr; } return sk_make_sp<SkSpecialSurface_Raster>(bm.info(), sk_ref_sp(bm.pixelRef()), subset, props); }
SkBitmapDevice::SkBitmapDevice(const SkBitmap& bitmap, const SkSurfaceProps& surfaceProps) : INHERITED(bitmap.info(), surfaceProps) , fBitmap(bitmap) { SkASSERT(valid_for_bitmap_device(bitmap.info(), nullptr)); fBitmap.lockPixels(); }
bool BitmapsEqual(const SkBitmap& a, const SkBitmap& b) { if (a.info() != b.info()) { return false; } const SkAutoLockPixels lockA(a), lockB(b); return 0 == memcmp(a.getPixels(), b.getPixels(), a.getSize()); }
// Check to see that the size of the bitmap that would be produced by // scaling by the given inverted matrix is less than the maximum allowed. static inline bool cache_size_okay(const SkBitmap& bm, const SkMatrix& invMat) { size_t maximumAllocation = SkResourceCache::GetSingleAllocationByteLimit(); if (0 == maximumAllocation) { return true; } // float matrixScaleFactor = 1.0 / (invMat.scaleX * invMat.scaleY); // return ((origBitmapSize * matrixScaleFactor) < maximumAllocationSize); // Skip the division step: return bm.info().getSafeSize(bm.info().minRowBytes()) < (maximumAllocation * invMat.getScaleX() * invMat.getScaleY()); }
void copy_to_g8(SkBitmap* dst, const SkBitmap& src) { SkASSERT(kBGRA_8888_SkColorType == src.colorType() || kRGBA_8888_SkColorType == src.colorType()); SkImageInfo grayInfo = src.info().makeColorType(kGray_8_SkColorType); dst->allocPixels(grayInfo); uint8_t* dst8 = (uint8_t*)dst->getPixels(); const uint32_t* src32 = (const uint32_t*)src.getPixels(); const int w = src.width(); const int h = src.height(); const bool isBGRA = (kBGRA_8888_SkColorType == src.colorType()); for (int y = 0; y < h; ++y) { if (isBGRA) { // BGRA for (int x = 0; x < w; ++x) { uint32_t s = src32[x]; dst8[x] = SkComputeLuminance((s >> 16) & 0xFF, (s >> 8) & 0xFF, s & 0xFF); } } else { // RGBA for (int x = 0; x < w; ++x) { uint32_t s = src32[x]; dst8[x] = SkComputeLuminance(s & 0xFF, (s >> 8) & 0xFF, (s >> 16) & 0xFF); } } src32 = (const uint32_t*)((const char*)src32 + src.rowBytes()); dst8 += dst->rowBytes(); }
bool SkCreateBitmapFromCGImage(SkBitmap* dst, CGImageRef image, SkISize* scaleToFit) { const int width = scaleToFit ? scaleToFit->width() : SkToInt(CGImageGetWidth(image)); const int height = scaleToFit ? scaleToFit->height() : SkToInt(CGImageGetHeight(image)); SkImageInfo info = SkImageInfo::MakeN32Premul(width, height); SkBitmap tmp; if (!tmp.allocPixels(info)) { return false; } if (!SkCopyPixelsFromCGImage(tmp.info(), tmp.rowBytes(), tmp.getPixels(), image)) { return false; } CGImageAlphaInfo cgInfo = CGImageGetAlphaInfo(image); switch (cgInfo) { case kCGImageAlphaNone: case kCGImageAlphaNoneSkipLast: case kCGImageAlphaNoneSkipFirst: SkASSERT(SkBitmap::ComputeIsOpaque(tmp)); tmp.setAlphaType(kOpaque_SkAlphaType); break; default: // we don't know if we're opaque or not, so compute it. if (SkBitmap::ComputeIsOpaque(tmp)) { tmp.setAlphaType(kOpaque_SkAlphaType); } } *dst = tmp; return true; }
sk_sp<SkImage> SkImage::MakeFromBitmap(const SkBitmap& bm) { SkPixelRef* pr = bm.pixelRef(); if (nullptr == pr) { return nullptr; } #if SK_SUPPORT_GPU if (GrTexture* tex = pr->getTexture()) { SkAutoTUnref<GrTexture> unrefCopy; if (!bm.isImmutable()) { tex = GrDeepCopyTexture(tex, SkBudgeted::kNo); if (nullptr == tex) { return nullptr; } unrefCopy.reset(tex); } const SkImageInfo info = bm.info(); return sk_make_sp<SkImage_Gpu>(info.width(), info.height(), bm.getGenerationID(), info.alphaType(), tex, sk_ref_sp(info.colorSpace()), SkBudgeted::kNo); } #endif // This will check for immutable (share or copy) return SkMakeImageFromRasterBitmap(bm); }
void SkSurface_Raster::onCopyOnWrite(ContentChangeMode mode) { // are we sharing pixelrefs with the image? sk_sp<SkImage> cached(this->refCachedImage(SkBudgeted::kNo, kNo_ForceUnique)); SkASSERT(cached); if (SkBitmapImageGetPixelRef(cached.get()) == fBitmap.pixelRef()) { SkASSERT(fWeOwnThePixels); if (kDiscard_ContentChangeMode == mode) { fBitmap.allocPixels(); } else { SkBitmap prev(fBitmap); fBitmap.allocPixels(); prev.lockPixels(); SkASSERT(prev.info() == fBitmap.info()); SkASSERT(prev.rowBytes() == fBitmap.rowBytes()); memcpy(fBitmap.getPixels(), prev.getPixels(), fBitmap.getSafeSize()); } SkASSERT(fBitmap.rowBytes() == fRowBytes); // be sure we always use the same value // Now fBitmap is a deep copy of itself (and therefore different from // what is being used by the image. Next we update the canvas to use // this as its backend, so we can't modify the image's pixels anymore. SkASSERT(this->getCachedCanvas()); this->getCachedCanvas()->getDevice()->replaceBitmapBackendForRasterSurface(fBitmap); } }
DEF_GPUTEST_FOR_RENDERING_CONTEXTS(SpecialImage_Gpu, reporter, ctxInfo) { GrContext* context = ctxInfo.grContext(); SkBitmap bm = create_bm(); const GrSurfaceDesc desc = GrImageInfoToSurfaceDesc(bm.info(), *context->caps()); sk_sp<GrTextureProxy> proxy(GrSurfaceProxy::MakeDeferred(context->resourceProvider(), desc, SkBudgeted::kNo, bm.getPixels(), bm.rowBytes())); if (!proxy) { return; } sk_sp<SkSpecialImage> fullSImg(SkSpecialImage::MakeDeferredFromGpu( context, SkIRect::MakeWH(kFullSize, kFullSize), kNeedNewImageUniqueID_SpecialImage, proxy, nullptr)); const SkIRect& subset = SkIRect::MakeXYWH(kPad, kPad, kSmallerSize, kSmallerSize); { sk_sp<SkSpecialImage> subSImg1(SkSpecialImage::MakeDeferredFromGpu( context, subset, kNeedNewImageUniqueID_SpecialImage, std::move(proxy), nullptr)); test_image(subSImg1, reporter, context, true, kPad, kFullSize); } { sk_sp<SkSpecialImage> subSImg2(fullSImg->makeSubset(subset)); test_image(subSImg2, reporter, context, true, kPad, kFullSize); } }
sk_sp<GrTextureProxy> GrUploadBitmapToTextureProxy(GrProxyProvider* proxyProvider, const SkBitmap& bitmap, SkColorSpace* dstColorSpace) { if (!bitmap.peekPixels(nullptr)) { return nullptr; } SkDestinationSurfaceColorMode colorMode = dstColorSpace ? SkDestinationSurfaceColorMode::kGammaAndColorSpaceAware : SkDestinationSurfaceColorMode::kLegacy; if (!SkImageInfoIsValid(bitmap.info(), colorMode)) { return nullptr; } // In non-ddl we will always instantiate right away. Thus we never want to copy the SkBitmap // even if it's mutable. In ddl, if the bitmap is mutable then we must make a copy since the // upload of the data to the gpu can happen at anytime and the bitmap may change by then. SkCopyPixelsMode cpyMode = proxyProvider->mutableBitmapsNeedCopy() ? kIfMutable_SkCopyPixelsMode : kNever_SkCopyPixelsMode; sk_sp<SkImage> image = SkMakeImageFromRasterBitmap(bitmap, cpyMode); return proxyProvider->createTextureProxy(std::move(image), kNone_GrSurfaceFlags, kTopLeft_GrSurfaceOrigin, 1, SkBudgeted::kYes, SkBackingFit::kExact); }
virtual Result onGetPixels(const SkImageInfo& info, void* pixels, size_t rowBytes, const Options&, SkPMColor ctableEntries[], int* ctableCount) override { SkMemoryStream stream(fData->data(), fData->size(), false); SkAutoTUnref<BareMemoryAllocator> allocator(SkNEW_ARGS(BareMemoryAllocator, (info, pixels, rowBytes))); fDecoder->setAllocator(allocator); fDecoder->setRequireUnpremultipliedColors(kUnpremul_SkAlphaType == info.alphaType()); SkBitmap bm; const SkImageDecoder::Result result = fDecoder->decode(&stream, &bm, info.colorType(), SkImageDecoder::kDecodePixels_Mode); if (SkImageDecoder::kFailure == result) { return kInvalidInput; } SkASSERT(info.colorType() == bm.info().colorType()); if (kIndex_8_SkColorType == info.colorType()) { SkASSERT(ctableEntries); SkColorTable* ctable = bm.getColorTable(); if (NULL == ctable) { return kInvalidConversion; } const int count = ctable->count(); memcpy(ctableEntries, ctable->readColors(), count * sizeof(SkPMColor)); *ctableCount = count; } if (SkImageDecoder::kPartialSuccess == result) { return kIncompleteInput; } return kSuccess; }
SkBitmapDevice* SkBitmapDevice::Create(const SkImageInfo& origInfo, const SkSurfaceProps& surfaceProps) { SkAlphaType newAT = origInfo.alphaType(); if (!valid_for_bitmap_device(origInfo, &newAT)) { return NULL; } const SkImageInfo info = origInfo.makeAlphaType(newAT); SkBitmap bitmap; if (kUnknown_SkColorType == info.colorType()) { if (!bitmap.setInfo(info)) { return NULL; } } else { if (!bitmap.tryAllocPixels(info)) { return NULL; } if (!bitmap.info().isOpaque()) { bitmap.eraseColor(SK_ColorTRANSPARENT); } } return SkNEW_ARGS(SkBitmapDevice, (bitmap, surfaceProps)); }
SkBitmapDevice* SkBitmapDevice::Create(const SkImageInfo& origInfo, const SkDeviceProperties* props) { SkImageInfo info = origInfo; if (!valid_for_bitmap_device(info, &info.fAlphaType)) { return NULL; } SkBitmap bitmap; if (kUnknown_SkColorType == info.colorType()) { if (!bitmap.setInfo(info)) { return NULL; } } else { if (!bitmap.allocPixels(info)) { return NULL; } if (!bitmap.info().isOpaque()) { bitmap.eraseColor(SK_ColorTRANSPARENT); } } if (props) { return SkNEW_ARGS(SkBitmapDevice, (bitmap, *props)); } else { return SkNEW_ARGS(SkBitmapDevice, (bitmap)); } }
static inline bool almost_equals(const SkBitmap& a, const SkBitmap& b, int tolerance) { if (a.info() != b.info()) { return false; } SkASSERT(kN32_SkColorType == a.colorType()); for (int y = 0; y < a.height(); y++) { for (int x = 0; x < a.width(); x++) { if (!almost_equals(*a.getAddr32(x, y), *b.getAddr32(x, y), tolerance)) { return false; } } } return true; }
static void generate_bitmap_texture_desc(const SkBitmap& bitmap, GrSurfaceDesc* desc) { desc->fFlags = kNone_GrSurfaceFlags; desc->fWidth = bitmap.width(); desc->fHeight = bitmap.height(); desc->fConfig = SkImageInfo2GrPixelConfig(bitmap.info()); desc->fSampleCnt = 0; }
// Test out the SkSpecialImage::makeTextureImage entry point DEF_GPUTEST_FOR_RENDERING_CONTEXTS(SpecialImage_MakeTexture, reporter, ctxInfo) { GrContext* context = ctxInfo.grContext(); SkBitmap bm = create_bm(); const SkIRect& subset = SkIRect::MakeXYWH(kPad, kPad, kSmallerSize, kSmallerSize); { // raster sk_sp<SkSpecialImage> rasterImage(SkSpecialImage::MakeFromRaster( SkIRect::MakeWH(kFullSize, kFullSize), bm)); { sk_sp<SkSpecialImage> fromRaster(rasterImage->makeTextureImage(context)); test_texture_backed(reporter, rasterImage, fromRaster); } { sk_sp<SkSpecialImage> subRasterImage(rasterImage->makeSubset(subset)); sk_sp<SkSpecialImage> fromSubRaster(subRasterImage->makeTextureImage(context)); test_texture_backed(reporter, subRasterImage, fromSubRaster); } } { // gpu const GrSurfaceDesc desc = GrImageInfoToSurfaceDesc(bm.info(), *context->caps()); sk_sp<GrTextureProxy> proxy(GrSurfaceProxy::MakeDeferred(context->resourceProvider(), desc, SkBudgeted::kNo, bm.getPixels(), bm.rowBytes())); if (!proxy) { return; } sk_sp<SkSpecialImage> gpuImage(SkSpecialImage::MakeDeferredFromGpu( context, SkIRect::MakeWH(kFullSize, kFullSize), kNeedNewImageUniqueID_SpecialImage, std::move(proxy), nullptr)); { sk_sp<SkSpecialImage> fromGPU(gpuImage->makeTextureImage(context)); test_texture_backed(reporter, gpuImage, fromGPU); } { sk_sp<SkSpecialImage> subGPUImage(gpuImage->makeSubset(subset)); sk_sp<SkSpecialImage> fromSubGPU(subGPUImage->makeTextureImage(context)); test_texture_backed(reporter, subGPUImage, fromSubGPU); } } }
static void md5(const SkBitmap& bm, SkMD5::Digest* digest) { SkAutoLockPixels autoLockPixels(bm); SkASSERT(bm.getPixels()); SkMD5 md5; size_t rowLen = bm.info().bytesPerPixel() * bm.width(); for (int y = 0; y < bm.height(); ++y) { md5.update(static_cast<uint8_t*>(bm.getAddr(0, y)), rowLen); } md5.finish(*digest); }
bool SkMatrixConvolutionImageFilter::onFilterImage(Proxy* proxy, const SkBitmap& source, const Context& ctx, SkBitmap* result, SkIPoint* offset) const { SkBitmap src = source; SkIPoint srcOffset = SkIPoint::Make(0, 0); if (getInput(0) && !getInput(0)->filterImage(proxy, source, ctx, &src, &srcOffset)) { return false; } if (src.colorType() != kN32_SkColorType) { return false; } SkIRect bounds; if (!this->applyCropRect(ctx, proxy, src, &srcOffset, &bounds, &src)) { return false; } if (!fConvolveAlpha && !src.isOpaque()) { src = unpremultiplyBitmap(src); } SkAutoLockPixels alp(src); if (!src.getPixels()) { return false; } if (!result->tryAllocPixels(src.info().makeWH(bounds.width(), bounds.height()))) { return false; } offset->fX = bounds.fLeft; offset->fY = bounds.fTop; bounds.offset(-srcOffset); SkIRect interior = SkIRect::MakeXYWH(bounds.left() + fKernelOffset.fX, bounds.top() + fKernelOffset.fY, bounds.width() - fKernelSize.fWidth + 1, bounds.height() - fKernelSize.fHeight + 1); SkIRect top = SkIRect::MakeLTRB(bounds.left(), bounds.top(), bounds.right(), interior.top()); SkIRect bottom = SkIRect::MakeLTRB(bounds.left(), interior.bottom(), bounds.right(), bounds.bottom()); SkIRect left = SkIRect::MakeLTRB(bounds.left(), interior.top(), interior.left(), interior.bottom()); SkIRect right = SkIRect::MakeLTRB(interior.right(), interior.top(), bounds.right(), interior.bottom()); filterBorderPixels(src, result, top, bounds); filterBorderPixels(src, result, left, bounds); filterInteriorPixels(src, result, interior, bounds); filterBorderPixels(src, result, right, bounds); filterBorderPixels(src, result, bottom, bounds); return true; }
/* Encode bitmaps via CGImageDestination. We setup a DataConsumer which writes to our SkWStream. Since we don't reference/own the SkWStream, our consumer must only live for the duration of the onEncode() method. */ bool SkEncodeImageWithCG(SkWStream* stream, const SkPixmap& pixmap, SkEncodedImageFormat format) { SkBitmap bm; if (!bm.installPixels(pixmap)) { return false; } bm.setImmutable(); CFStringRef type; switch (format) { case SkEncodedImageFormat::kICO: type = kUTTypeICO; break; case SkEncodedImageFormat::kBMP: type = kUTTypeBMP; break; case SkEncodedImageFormat::kGIF: type = kUTTypeGIF; break; case SkEncodedImageFormat::kJPEG: type = kUTTypeJPEG; break; case SkEncodedImageFormat::kPNG: // PNG encoding an ARGB_4444 bitmap gives the following errors in GM: // <Error>: CGImageDestinationAddImage image could not be converted to destination // format. // <Error>: CGImageDestinationFinalize image destination does not have enough images // So instead we copy to 8888. if (bm.colorType() == kARGB_4444_SkColorType) { SkBitmap bitmapN32; bitmapN32.allocPixels(bm.info().makeColorType(kN32_SkColorType)); bm.readPixels(bitmapN32.info(), bitmapN32.getPixels(), bitmapN32.rowBytes(), 0, 0); bm.swap(bitmapN32); } type = kUTTypePNG; break; default: return false; } CGImageDestinationRef dst = SkStreamToImageDestination(stream, type); if (nullptr == dst) { return false; } SkAutoTCallVProc<const void, CFRelease> ardst(dst); CGImageRef image = SkCreateCGImageRef(bm); if (nullptr == image) { return false; } SkAutoTCallVProc<CGImage, CGImageRelease> agimage(image); CGImageDestinationAddImage(dst, image, nullptr); return CGImageDestinationFinalize(dst); }
static SkBitmap createBitmapWithSpace(const SkBitmap& bitmap, int spaceWidth, int spaceHeight) { SkImageInfo info = bitmap.info(); info = SkImageInfo::Make(info.width() + spaceWidth, info.height() + spaceHeight, info.colorType(), kPremul_SkAlphaType); SkBitmap result; result.allocPixels(info); result.eraseColor(SK_ColorTRANSPARENT); bitmap.copyPixelsTo(reinterpret_cast<uint8_t*>(result.getPixels()), result.rowBytes() * result.height(), result.rowBytes()); return result; }
DEF_TEST(Picture_EncodedData, reporter) { // Create a bitmap that will be encoded. SkBitmap original; make_bm(&original, 100, 100, SK_ColorBLUE, true); SkDynamicMemoryWStream wStream; if (!SkImageEncoder::EncodeStream(&wStream, original, SkImageEncoder::kPNG_Type, 100)) { return; } SkAutoDataUnref data(wStream.copyToData()); SkBitmap bm; bool installSuccess = SkDEPRECATED_InstallDiscardablePixelRef(data, &bm); REPORTER_ASSERT(reporter, installSuccess); // Write both bitmaps to pictures, and ensure that the resulting data streams are the same. // Flattening original will follow the old path of performing an encode, while flattening bm // will use the already encoded data. SkAutoDataUnref picture1(serialized_picture_from_bitmap(original)); SkAutoDataUnref picture2(serialized_picture_from_bitmap(bm)); REPORTER_ASSERT(reporter, picture1->equals(picture2)); // Now test that a parse error was generated when trying to create a new SkPicture without // providing a function to decode the bitmap. ErrorContext context; context.fErrors = 0; context.fReporter = reporter; SkSetErrorCallback(assert_one_parse_error_cb, &context); SkMemoryStream pictureStream(picture1); SkClearLastError(); sk_sp<SkPicture> pictureFromStream(SkPicture::MakeFromStream(&pictureStream, nullptr)); REPORTER_ASSERT(reporter, pictureFromStream.get() != nullptr); SkClearLastError(); SkSetErrorCallback(nullptr, nullptr); // Test that using the version of CreateFromStream that just takes a stream also decodes the // bitmap. Drawing this picture should look exactly like the original bitmap. SkMD5::Digest referenceDigest; md5(original, &referenceDigest); SkBitmap dst; dst.allocPixels(original.info()); dst.eraseColor(SK_ColorRED); SkCanvas canvas(dst); pictureStream.rewind(); pictureFromStream = SkPicture::MakeFromStream(&pictureStream); canvas.drawPicture(pictureFromStream.get()); SkMD5::Digest digest2; md5(dst, &digest2); REPORTER_ASSERT(reporter, referenceDigest == digest2); }
sk_sp<SkSpecialImage> SkSpecialImage::MakeFromRaster(const SkIRect& subset, const SkBitmap& bm, const SkSurfaceProps* props) { SkASSERT(rect_fits(subset, bm.width(), bm.height())); if (!bm.pixelRef()) { return nullptr; } const SkBitmap* srcBM = &bm; SkBitmap tmp; // ImageFilters only handle N32 at the moment, so force our src to be that if (!valid_for_imagefilters(bm.info())) { if (!tmp.tryAllocPixels(bm.info().makeColorType(kN32_SkColorType)) || !bm.readPixels(tmp.info(), tmp.getPixels(), tmp.rowBytes(), 0, 0)) { return nullptr; } srcBM = &tmp; } return sk_make_sp<SkSpecialImage_Raster>(subset, *srcBM, props); }
// A contructor-type function that returns NULL on failure. This // prevents the returned SkImageGenerator from ever being in a bad // state. Called by both Create() functions SkImageGenerator* CreateDecodingImageGenerator( SkData* data, SkStreamRewindable* stream, const SkDecodingImageGenerator::Options& opts) { SkASSERT(stream); SkAutoTUnref<SkStreamRewindable> autoStream(stream); // always unref this. if (opts.fUseRequestedColorType && (kIndex_8_SkColorType == opts.fRequestedColorType)) { // We do not support indexed color with SkImageGenerators, return NULL; } SkAssertResult(autoStream->rewind()); SkAutoTDelete<SkImageDecoder> decoder(SkImageDecoder::Factory(autoStream)); if (NULL == decoder.get()) { return NULL; } SkBitmap bitmap; decoder->setSampleSize(opts.fSampleSize); decoder->setRequireUnpremultipliedColors(opts.fRequireUnpremul); if (!decoder->decode(stream, &bitmap, SkImageDecoder::kDecodeBounds_Mode)) { return NULL; } if (bitmap.config() == SkBitmap::kNo_Config) { return NULL; } SkImageInfo info = bitmap.info(); if (!opts.fUseRequestedColorType) { // Use default if (kIndex_8_SkColorType == bitmap.colorType()) { // We don't support kIndex8 because we don't support // colortables in this workflow. info.fColorType = kN32_SkColorType; } } else { if (!bitmap.canCopyTo(opts.fRequestedColorType)) { SkASSERT(bitmap.colorType() != opts.fRequestedColorType); return NULL; // Can not translate to needed config. } info.fColorType = opts.fRequestedColorType; } if (opts.fRequireUnpremul && info.fAlphaType != kOpaque_SkAlphaType) { info.fAlphaType = kUnpremul_SkAlphaType; } return SkNEW_ARGS(DecodingImageGenerator, (data, autoStream.detach(), info, opts.fSampleSize, opts.fDitherImage)); }
bool WriteTask::Expectations::check(const Task& task, SkBitmap bitmap) const { if (!FLAGS_writePath.isEmpty() && 0 == strcmp(FLAGS_writePath[0], fRoot)) { SkDebugf("We seem to be reading and writing %s concurrently. This won't work.\n", fRoot); return false; } const SkString path = path_to_expected_image(fRoot, task); SkBitmap expected; if (!PngAndRaw::Decode(path.c_str(), bitmap.info(), &expected)) { return false; } return BitmapsEqual(expected, bitmap); }
/* * Modulo internal errors, this should always succeed *if* the matrix is downscaling * (in this case, we have the inverse, so it succeeds if fInvMatrix is upscaling) */ bool SkDefaultBitmapControllerState::processMediumRequest(const SkBitmap& origBitmap) { SkASSERT(fQuality <= kMedium_SkFilterQuality); if (fQuality != kMedium_SkFilterQuality) { return false; } // Our default return state is to downgrade the request to Low, w/ or w/o setting fBitmap // to a valid bitmap. fQuality = kLow_SkFilterQuality; SkSize invScaleSize; if (!fInvMatrix.decomposeScale(&invScaleSize, NULL)) { return false; } SkScalar invScale = SkScalarSqrt(invScaleSize.width() * invScaleSize.height()); if (invScale > SK_Scalar1) { fCurrMip.reset(SkMipMapCache::FindAndRef(origBitmap)); if (NULL == fCurrMip.get()) { fCurrMip.reset(SkMipMapCache::AddAndRef(origBitmap)); if (NULL == fCurrMip.get()) { return false; } } // diagnostic for a crasher... if (NULL == fCurrMip->data()) { sk_throw(); } SkScalar levelScale = SkScalarInvert(invScale); SkMipMap::Level level; if (fCurrMip->extractLevel(levelScale, &level)) { SkScalar invScaleFixup = level.fScale; fInvMatrix.postScale(invScaleFixup, invScaleFixup); const SkImageInfo info = origBitmap.info().makeWH(level.fWidth, level.fHeight); // todo: if we could wrap the fCurrMip in a pixelref, then we could just install // that here, and not need to explicitly track it ourselves. return fResultBitmap.installPixels(info, level.fPixels, level.fRowBytes); } else { // failed to extract, so release the mipmap fCurrMip.reset(NULL); } } return false; }
static void Bitmap_reconfigure(JNIEnv* env, jobject clazz, jlong bitmapHandle, jint width, jint height, jint configHandle, jint allocSize, jboolean requestPremul) { SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle); SkColorType colorType = GraphicsJNI::legacyBitmapConfigToColorType(configHandle); // ARGB_4444 is a deprecated format, convert automatically to 8888 if (colorType == kARGB_4444_SkColorType) { colorType = kN32_SkColorType; } if (width * height * SkColorTypeBytesPerPixel(colorType) > allocSize) { // done in native as there's no way to get BytesPerPixel in Java doThrowIAE(env, "Bitmap not large enough to support new configuration"); return; } SkPixelRef* ref = bitmap->pixelRef(); ref->ref(); SkAlphaType alphaType; if (bitmap->colorType() != kRGB_565_SkColorType && bitmap->alphaType() == kOpaque_SkAlphaType) { // If the original bitmap was set to opaque, keep that setting, unless it // was 565, which is required to be opaque. alphaType = kOpaque_SkAlphaType; } else { // Otherwise respect the premultiplied request. alphaType = requestPremul ? kPremul_SkAlphaType : kUnpremul_SkAlphaType; } bitmap->setInfo(SkImageInfo::Make(width, height, colorType, alphaType)); // FIXME: Skia thinks of an SkPixelRef as having a constant SkImageInfo (except for // its alphatype), so it would make more sense from Skia's perspective to create a // new SkPixelRef. That said, libhwui uses the pointer to the SkPixelRef as a key // for its cache, so it won't realize this is the same Java Bitmap. SkImageInfo& info = const_cast<SkImageInfo&>(ref->info()); // Use the updated from the SkBitmap, which may have corrected an invalid alphatype. // (e.g. 565 non-opaque) info = bitmap->info(); bitmap->setPixelRef(ref); // notifyPixelsChanged will increment the generation ID even though the actual pixel data // hasn't been touched. This signals the renderer that the bitmap (including width, height, // colortype and alphatype) has changed. ref->notifyPixelsChanged(); ref->unref(); }
// Test that some SkCodecs do not attempt to read input beyond the logical // end of the data. Some other SkCodecs do, but some Android apps rely on not // doing so for PNGs. Test on other formats that work. DEF_TEST(Codec_end, r) { for (const char* path : { "images/plane.png", "images/yellow_rose.png", "images/plane_interlaced.png", "images/google_chrome.ico", "images/color_wheel.ico", "images/mandrill.wbmp", "images/randPixels.bmp", }) { sk_sp<SkData> data = GetResourceAsData(path); if (!data) { continue; } const int kNumImages = 2; const size_t size = data->size(); sk_sp<SkData> multiData = SkData::MakeUninitialized(size * kNumImages); void* dst = multiData->writable_data(); for (int i = 0; i < kNumImages; i++) { memcpy(SkTAddOffset<void>(dst, size * i), data->data(), size); } data.reset(); SkMemoryStream stream(std::move(multiData)); for (int i = 0; i < kNumImages; ++i) { std::unique_ptr<SkCodec> codec(SkCodec::MakeFromStream( skstd::make_unique<UnowningStream>(&stream))); if (!codec) { ERRORF(r, "Failed to create a codec from %s, iteration %i", path, i); continue; } auto info = codec->getInfo().makeColorType(kN32_SkColorType); SkBitmap bm; bm.allocPixels(info); auto result = codec->getPixels(bm.info(), bm.getPixels(), bm.rowBytes()); if (result != SkCodec::kSuccess) { ERRORF(r, "Failed to getPixels from %s, iteration %i error %i", path, i, result); continue; } } } }
// FIXME: This should be refactored to SkImageFilterUtils for // use by other filters. For now, we assume the input is always // premultiplied and unpremultiply it static SkBitmap unpremultiplyBitmap(const SkBitmap& src) { SkAutoLockPixels alp(src); if (!src.getPixels()) { return SkBitmap(); } SkBitmap result; if (!result.tryAllocPixels(src.info())) { return SkBitmap(); } for (int y = 0; y < src.height(); ++y) { const uint32_t* srcRow = src.getAddr32(0, y); uint32_t* dstRow = result.getAddr32(0, y); for (int x = 0; x < src.width(); ++x) { dstRow[x] = SkUnPreMultiply::PMColorToColor(srcRow[x]); } } return result; }