Example #1
0
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;
}
Example #2
0
SkBitmapDevice::SkBitmapDevice(const SkBitmap& bitmap)
    : INHERITED(bitmap.info(), SkSurfaceProps(SkSurfaceProps::kLegacyFontHost_InitType))
    , fBitmap(bitmap)
{
    SkASSERT(valid_for_bitmap_device(bitmap.info(), nullptr));
    fBitmap.lockPixels();
}
Example #3
0
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);
}
Example #4
0
SkBitmapDevice::SkBitmapDevice(const SkBitmap& bitmap, const SkSurfaceProps& surfaceProps)
    : INHERITED(bitmap.info(), surfaceProps)
    , fBitmap(bitmap)
{
    SkASSERT(valid_for_bitmap_device(bitmap.info(), nullptr));
    fBitmap.lockPixels();
}
Example #5
0
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());
}
Example #6
0
// 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;
}
Example #9
0
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);
}
Example #10
0
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);
    }
}
Example #11
0
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);
    }
}
Example #12
0
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;
    }
Example #14
0
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));
}
Example #15
0
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));
    }
}
Example #16
0
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;
}
Example #17
0
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;
}
Example #18
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);
        }
    }
}
Example #19
0
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);
}
Example #22
0
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;
}
Example #23
0
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));
}
Example #26
0
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);
}
Example #27
0
/*
 *  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;
}
Example #28
0
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();
}
Example #29
0
// 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;
}