static void check_fill(skiatest::Reporter* r, const SkImageInfo& imageInfo, uint32_t startRow, uint32_t endRow, size_t rowBytes, uint32_t offset, uint32_t colorOrIndex, SkPMColor* colorTable) { // Calculate the total size of the image in bytes. Use the smallest possible size. // The offset value tells us to adjust the pointer from the memory we allocate in order // to test on different memory alignments. If offset is nonzero, we need to increase the // size of the memory we allocate in order to make sure that we have enough. We are // still allocating the smallest possible size. const size_t totalBytes = imageInfo.getSafeSize(rowBytes) + offset; // Create fake image data where every byte has a value of 0 SkAutoTDeleteArray<uint8_t> storage(SkNEW_ARRAY(uint8_t, totalBytes)); memset(storage.get(), 0, totalBytes); // Adjust the pointer in order to test on different memory alignments uint8_t* imageData = storage.get() + offset; uint8_t* imageStart = imageData + rowBytes * startRow; // Fill image with the fill value starting at the indicated row SkSwizzler::Fill(imageStart, imageInfo, rowBytes, endRow - startRow + 1, colorOrIndex, colorTable); // Ensure that the pixels are filled properly // The bots should catch any memory corruption uint8_t* indexPtr = imageData + startRow * rowBytes; uint32_t* colorPtr = (uint32_t*) indexPtr; for (uint32_t y = startRow; y <= endRow; y++) { for (int32_t x = 0; x < imageInfo.width(); x++) { if (kIndex_8_SkColorType == imageInfo.colorType()) { REPORTER_ASSERT(r, kFillIndex == indexPtr[x]); } else { REPORTER_ASSERT(r, kFillColor == colorPtr[x]); } } indexPtr += rowBytes; colorPtr = (uint32_t*) indexPtr; } }
static void apply_premul(const SkImageInfo& info, void* pixels, size_t rowBytes) { switch (info.colorType()) { case kRGBA_8888_SkColorType: case kBGRA_8888_SkColorType: break; default: return; // nothing to do } // SkColor is not necesarily RGBA or BGRA, but it is one of them on little-endian, // and in either case, the alpha-byte is always in the same place, so we can safely call // SkPreMultiplyColor() // SkColor* row = (SkColor*)pixels; for (int y = 0; y < info.height(); ++y) { for (int x = 0; x < info.width(); ++x) { row[x] = SkPreMultiplyColor(row[x]); } } }
bool SkImageGeneratorCG::onGetPixels(const SkImageInfo& info, void* pixels, size_t rowBytes, SkPMColor ctable[], int* ctableCount) { if (kN32_SkColorType != info.colorType()) { // FIXME: Support other colorTypes. return false; } switch (info.alphaType()) { case kOpaque_SkAlphaType: if (kOpaque_SkAlphaType != this->getInfo().alphaType()) { return false; } break; case kPremul_SkAlphaType: break; default: return false; } CGImageRef image = CGImageSourceCreateImageAtIndex((CGImageSourceRef) fImageSrc.get(), 0, nullptr); if (!image) { return false; } SkAutoTCallVProc<CGImage, CGImageRelease> autoImage(image); // FIXME: Using this function (as opposed to swizzling ourselves) greatly // restricts the color and alpha types that we support. If we // swizzle ourselves, we can add support for: // kUnpremul_SkAlphaType // 16-bit per component RGBA // kGray_8_SkColorType // kIndex_8_SkColorType // Additionally, it would be interesting to compare the performance // of SkSwizzler with CG's built in swizzler. if (!SkCopyPixelsFromCGImage(info, rowBytes, pixels, image)) { return false; } return true; }
void SkJpegCodec::initializeSwizzler(const SkImageInfo& dstInfo, const Options& options) { SkSwizzler::SrcConfig srcConfig = SkSwizzler::kUnknown; if (JCS_CMYK == fDecoderMgr->dinfo()->out_color_space) { srcConfig = SkSwizzler::kCMYK; } else { // If the out_color_space is not CMYK, the only reason we would need a swizzler is // for sampling and/or subsetting. switch (dstInfo.colorType()) { case kGray_8_SkColorType: srcConfig = SkSwizzler::kNoOp8; break; case kN32_SkColorType: srcConfig = SkSwizzler::kNoOp32; break; case kRGB_565_SkColorType: srcConfig = SkSwizzler::kNoOp16; break; default: // This function should only be called if the colorType is supported by jpeg SkASSERT(false); } } if (JCS_RGB == fDecoderMgr->dinfo()->out_color_space) { srcConfig = SkSwizzler::kRGB; } Options swizzlerOptions = options; if (options.fSubset) { // Use fSwizzlerSubset if this is a subset decode. This is necessary in the case // where libjpeg-turbo provides a subset and then we need to subset it further. // Also, verify that fSwizzlerSubset is initialized and valid. SkASSERT(!fSwizzlerSubset.isEmpty() && fSwizzlerSubset.x() <= options.fSubset->x() && fSwizzlerSubset.width() == options.fSubset->width()); swizzlerOptions.fSubset = &fSwizzlerSubset; } fSwizzler.reset(SkSwizzler::CreateSwizzler(srcConfig, nullptr, dstInfo, swizzlerOptions)); SkASSERT(fSwizzler); fStorage.reset(get_row_bytes(fDecoderMgr->dinfo())); fSrcRow = fStorage.get(); }
SkMallocPixelRef::SkMallocPixelRef(const SkImageInfo& info, void* storage, size_t rowBytes, SkColorTable* ctable, bool ownsPixels) : INHERITED(info) , fReleaseProc(ownsPixels ? sk_free_releaseproc : NULL) , fReleaseProcContext(NULL) { // This constructor is now DEPRICATED. SkASSERT(is_valid(info, ctable)); SkASSERT(rowBytes >= info.minRowBytes()); if (kIndex_8_SkColorType != info.colorType()) { ctable = NULL; } fStorage = storage; fCTable = ctable; fRB = rowBytes; SkSafeRef(ctable); this->setPreLocked(fStorage, rowBytes, fCTable); }
static bool is_valid(const SkImageInfo& info, SkColorTable* ctable) { if (info.width() < 0 || info.height() < 0 || (unsigned)info.colorType() > (unsigned)kLastEnum_SkColorType || (unsigned)info.alphaType() > (unsigned)kLastEnum_SkAlphaType) { return false; } // these seem like good checks, but currently we have (at least) tests // that expect the pixelref to succeed even when there is a mismatch // with colortables. fix? #if 0 if (kIndex8_SkColorType == info.fColorType && nullptr == ctable) { return false; } if (kIndex8_SkColorType != info.fColorType && ctable) { return false; } #endif return true; }
bool DecodingImageGenerator::onGetPixels(const SkImageInfo& info, void* pixels, size_t rowBytes, SkPMColor ctable[], int* ctableCount) { TRACE_EVENT1("blink", "DecodingImageGenerator::getPixels", "index", static_cast<int>(m_frameIndex)); // Implementation doesn't support scaling yet so make sure we're not given a different size. if (info.width() != getInfo().width() || info.height() != getInfo().height()) return false; if (info.colorType() != getInfo().colorType()) { // ImageFrame may have changed the owning SkBitmap to kOpaque_SkAlphaType after sniffing the encoded data, so if we see a request // for opaque, that is ok even if our initial alphatype was not opaque. return false; } PlatformInstrumentation::willDecodeLazyPixelRef(m_generationId); bool decoded = m_frameGenerator->decodeAndScale(getInfo(), m_frameIndex, pixels, rowBytes); PlatformInstrumentation::didDecodeLazyPixelRef(); return decoded; }
void SkJpegCodec::allocateStorage(const SkImageInfo& dstInfo) { size_t swizzleBytes = 0; if (fSwizzler) { swizzleBytes = get_row_bytes(fDecoderMgr->dinfo()); SkASSERT(!fColorXform || SkIsAlign4(swizzleBytes)); } size_t xformBytes = 0; if (kRGBA_F16_SkColorType == dstInfo.colorType()) { SkASSERT(fColorXform); xformBytes = dstInfo.width() * sizeof(uint32_t); } size_t totalBytes = swizzleBytes + xformBytes; if (totalBytes > 0) { fStorage.reset(totalBytes); fSwizzleSrcRow = (swizzleBytes > 0) ? fStorage.get() : nullptr; fColorXformSrcRow = (xformBytes > 0) ? SkTAddOffset<uint32_t>(fStorage.get(), swizzleBytes) : nullptr; } }
bool SkInstallDiscardablePixelRef(SkImageGenerator* generator, SkBitmap* dst, SkDiscardableMemory::Factory* factory) { SkImageInfo info; SkAutoTDelete<SkImageGenerator> autoGenerator(generator); if ((NULL == autoGenerator.get()) || (!autoGenerator->getInfo(&info)) || (!dst->setInfo(info))) { return false; } // Since dst->setInfo() may have changed/fixed-up info, we copy it back from that bitmap info = dst->info(); SkASSERT(info.colorType() != kUnknown_SkColorType); if (dst->empty()) { // Use a normal pixelref. return dst->tryAllocPixels(); } SkAutoTUnref<SkDiscardablePixelRef> ref( SkNEW_ARGS(SkDiscardablePixelRef, (info, autoGenerator.detach(), dst->rowBytes(), factory))); dst->setPixelRef(ref); return true; }
SkMallocPixelRef::SkMallocPixelRef(const SkImageInfo& info, void* storage, size_t rowBytes, SkColorTable* ctable, SkMallocPixelRef::ReleaseProc proc, void* context) : INHERITED(info) , fReleaseProc(proc) , fReleaseProcContext(context) { SkASSERT(is_valid(info, ctable)); SkASSERT(rowBytes >= info.minRowBytes()); if (kIndex_8_SkColorType != info.colorType()) { ctable = NULL; } fStorage = storage; fCTable = ctable; fRB = rowBytes; SkSafeRef(ctable); this->setPreLocked(fStorage, rowBytes, fCTable); }
static void clamp_if_necessary(const SkImageInfo& info, void* pixels) { if (kRGBA_F16_SkColorType != info.colorType()) { return; } for (int y = 0; y < info.height(); y++) { for (int x = 0; x < info.width(); x++) { uint64_t pixel = ((uint64_t*) pixels)[y * info.width() + x]; Sk4f rgba = SkHalfToFloat_finite_ftz(pixel); if (kUnpremul_SkAlphaType == info.alphaType()) { rgba = Sk4f::Max(0.0f, Sk4f::Min(rgba, 1.0f)); } else { SkASSERT(kPremul_SkAlphaType == info.alphaType()); rgba = Sk4f::Max(0.0f, Sk4f::Min(rgba, rgba[3])); } SkFloatToHalf_finite_ftz(rgba).store(&pixel); ((uint64_t*) pixels)[y * info.width() + x] = pixel; } } }
void SkCodec::fillIncompleteImage(const SkImageInfo& info, void* dst, size_t rowBytes, ZeroInitialized zeroInit, int linesRequested, int linesDecoded) { void* fillDst; const uint32_t fillValue = this->getFillValue(info.colorType()); const int linesRemaining = linesRequested - linesDecoded; SkSampler* sampler = this->getSampler(false); int fillWidth = info.width(); if (fOptions.fSubset) { fillWidth = fOptions.fSubset->width(); } switch (this->getScanlineOrder()) { case kTopDown_SkScanlineOrder: case kNone_SkScanlineOrder: { const SkImageInfo fillInfo = info.makeWH(fillWidth, linesRemaining); fillDst = SkTAddOffset<void>(dst, linesDecoded * rowBytes); fill_proc(fillInfo, fillDst, rowBytes, fillValue, zeroInit, sampler); break; } case kBottomUp_SkScanlineOrder: { fillDst = dst; const SkImageInfo fillInfo = info.makeWH(fillWidth, linesRemaining); fill_proc(fillInfo, fillDst, rowBytes, fillValue, zeroInit, sampler); break; } case kOutOfOrder_SkScanlineOrder: { SkASSERT(1 == linesRequested || this->getInfo().height() == linesRequested); const SkImageInfo fillInfo = info.makeWH(fillWidth, 1); for (int srcY = linesDecoded; srcY < linesRequested; srcY++) { fillDst = SkTAddOffset<void>(dst, this->outputScanline(srcY) * rowBytes); fill_proc(fillInfo, fillDst, rowBytes, fillValue, zeroInit, sampler); } break; } } }
bool SkLinearGradient::LinearGradient4fContext::onChooseBlitProcs(const SkImageInfo& info, BlitState* state) { SkXfermode::Mode mode; if (!SkXfermode::AsMode(state->fXfer, &mode)) { return false; } if (mode != SkXfermode::kSrc_Mode && !(mode == SkXfermode::kSrcOver_Mode && (fFlags & kOpaqueAlpha_Flag))) { return false; } switch (info.colorType()) { case kN32_SkColorType: state->fBlitBW = D32_BlitBW; return true; case kRGBA_F16_SkColorType: state->fBlitBW = D64_BlitBW; return true; default: return false; } }
bool SkImageGeneratorWIC::onGetPixels(const SkImageInfo& info, void* pixels, size_t rowBytes, SkPMColor ctable[], int* ctableCount) { if (kN32_SkColorType != info.colorType()) { return false; } // Create a format converter. SkTScopedComPtr<IWICFormatConverter> formatConverter; HRESULT hr = fImagingFactory->CreateFormatConverter(&formatConverter); if (FAILED(hr)) { return false; } GUID format = GUID_WICPixelFormat32bppPBGRA; if (kUnpremul_SkAlphaType == info.alphaType()) { format = GUID_WICPixelFormat32bppBGRA; } hr = formatConverter->Initialize(fImageSource.get(), format, WICBitmapDitherTypeNone, nullptr, 0.0, WICBitmapPaletteTypeCustom); if (FAILED(hr)) { return false; } // Treat the format converter as an image source. SkTScopedComPtr<IWICBitmapSource> formatConverterSrc; hr = formatConverter->QueryInterface(IID_PPV_ARGS(&formatConverterSrc)); if (FAILED(hr)) { return false; } // Set the destination pixels. hr = formatConverterSrc->CopyPixels(nullptr, (UINT) rowBytes, (UINT) rowBytes * info.height(), (BYTE*) pixels); return SUCCEEDED(hr); }
bool SkImageFilter::filterInputGPU(int index, SkImageFilter::Proxy* proxy, const SkBitmap& src, const Context& origCtx, SkBitmap* result, SkIPoint* offset, bool relaxSizeConstraint) const { SkImageFilter* input = this->getInput(index); if (!input) { return true; } // Ensure that GrContext calls under filterImage and filterImageGPU below will see an identity // matrix with no clip and that the matrix, clip, and render target set before this function was // called are restored before we return to the caller. GrContext* context = src.getTexture()->getContext(); SizeConstraint constraint = origCtx.sizeConstraint(); if (relaxSizeConstraint && (kExact_SizeConstraint == constraint)) { constraint = kApprox_SizeConstraint; } Context ctx(origCtx.ctm(), origCtx.clipBounds(), origCtx.cache(), constraint); if (input->filterImage(proxy, src, this->mapContext(ctx), result, offset)) { if (!result->getTexture()) { const SkImageInfo info = result->info(); if (kUnknown_SkColorType == info.colorType()) { return false; } SkAutoTUnref<GrTexture> resultTex( GrRefCachedBitmapTexture(context, *result, GrTextureParams::ClampNoFilter())); if (!resultTex) { return false; } result->setPixelRef(new SkGrPixelRef(info, resultTex))->unref(); } return true; } else { return false; } }
/* * Checks if the conversion between the input image and the requested output * image has been implemented * Sets the output color format */ bool SkHeifCodec::setOutputColorFormat(const SkImageInfo& dstInfo) { if (kUnknown_SkAlphaType == dstInfo.alphaType()) { return false; } if (kOpaque_SkAlphaType != dstInfo.alphaType()) { SkCodecPrintf("Warning: an opaque image should be decoded as opaque " "- it is being decoded as non-opaque, which will draw slower\n"); } switch (dstInfo.colorType()) { case kRGBA_8888_SkColorType: return fHeifDecoder->setOutputColor(kHeifColorFormat_RGBA_8888); case kBGRA_8888_SkColorType: return fHeifDecoder->setOutputColor(kHeifColorFormat_BGRA_8888); case kRGB_565_SkColorType: if (this->colorXform()) { return fHeifDecoder->setOutputColor(kHeifColorFormat_RGBA_8888); } else { return fHeifDecoder->setOutputColor(kHeifColorFormat_RGB565); } case kRGBA_F16_SkColorType: SkASSERT(this->colorXform()); if (!dstInfo.colorSpace()->gammaIsLinear()) { return false; } return fHeifDecoder->setOutputColor(kHeifColorFormat_RGBA_8888); default: return false; } }
bool SkLinearGradient::LinearGradient4fContext::onChooseBlitProcs(const SkImageInfo& info, BlitState* state) { SkXfermode::Mode mode; if (!SkXfermode::AsMode(state->fXfer, &mode)) { return false; } const SkGradientShaderBase& shader = static_cast<const SkGradientShaderBase&>(fShader); if (mode != SkXfermode::kSrc_Mode && !(mode == SkXfermode::kSrcOver_Mode && shader.colorsAreOpaque())) { return false; } switch (info.colorType()) { case kN32_SkColorType: state->fBlitBW = D32_BlitBW; return true; case kRGBA_F16_SkColorType: state->fBlitBW = D64_BlitBW; return true; default: return false; } }
void HelloWorldWindow::draw(SkCanvas* canvas) { drawContents(canvas); // in case we have queued drawing calls fContext->flush(); // Invalidate the window to force a redraw. Poor man's animation mechanism. this->inval(NULL); if (kRaster_DeviceType == fType) { // need to send the raster bits to the (gpu) window SkImage* snap = fSurface->newImageSnapshot(); size_t rowBytes; SkImageInfo info; const void* pixels = snap->peekPixels(&info, &rowBytes); fRenderTarget->writePixels(0, 0, snap->width(), snap->height(), SkImageInfo2GrPixelConfig(info.colorType(), info.alphaType(), info.profileType()), pixels, rowBytes, GrContext::kFlushWrites_PixelOp); SkSafeUnref(snap); } INHERITED::present(); }
bool SkDiscardablePixelRef::onNewLockPixels(LockRec* rec) { if (fDiscardableMemory != NULL) { if (fDiscardableMemory->lock()) { rec->fPixels = fDiscardableMemory->data(); rec->fColorTable = fCTable.get(); rec->fRowBytes = fRowBytes; return true; } SkDELETE(fDiscardableMemory); fDiscardableMemory = NULL; } const size_t size = this->info().getSafeSize(fRowBytes); if (fDMFactory != NULL) { fDiscardableMemory = fDMFactory->create(size); } else { fDiscardableMemory = SkDiscardableMemory::Create(size); } if (NULL == fDiscardableMemory) { return false; // Memory allocation failed. } void* pixels = fDiscardableMemory->data(); const SkImageInfo& info = this->info(); SkPMColor colors[256]; int colorCount = 0; #ifdef SK_SUPPORT_LEGACY_IMAGEGENERATORAPI if (!fGenerator->getPixels(info, pixels, fRowBytes)) { #else if (!fGenerator->getPixels(info, pixels, fRowBytes, colors, &colorCount)) { #endif fDiscardableMemory->unlock(); SkDELETE(fDiscardableMemory); fDiscardableMemory = NULL; return false; } // Note: our ctable is not purgable, as it is not stored in the discardablememory block. // This is because SkColorTable is refcntable, and therefore our caller could hold onto it // beyond the scope of a lock/unlock. If we change the API/lifecycle for SkColorTable, we // could move it into the block, but then again perhaps it is small enough that this doesn't // really matter. if (colorCount > 0) { fCTable.reset(SkNEW_ARGS(SkColorTable, (colors, colorCount))); } else { fCTable.reset(NULL); } rec->fPixels = pixels; rec->fColorTable = fCTable.get(); rec->fRowBytes = fRowBytes; return true; } void SkDiscardablePixelRef::onUnlockPixels() { fDiscardableMemory->unlock(); } bool SkInstallDiscardablePixelRef(SkImageGenerator* generator, SkBitmap* dst, SkDiscardableMemory::Factory* factory) { SkImageInfo info; SkAutoTDelete<SkImageGenerator> autoGenerator(generator); if ((NULL == autoGenerator.get()) || (!autoGenerator->getInfo(&info)) || (!dst->setInfo(info))) { return false; } // Since dst->setInfo() may have changed/fixed-up info, we copy it back from that bitmap info = dst->info(); SkASSERT(info.colorType() != kUnknown_SkColorType); if (dst->empty()) { // Use a normal pixelref. return dst->tryAllocPixels(); } SkAutoTUnref<SkDiscardablePixelRef> ref( SkNEW_ARGS(SkDiscardablePixelRef, (info, autoGenerator.detach(), dst->rowBytes(), factory))); dst->setPixelRef(ref); return true; }
static SkImageInfo validate_info(const SkImageInfo& info) { SkAlphaType newAlphaType = info.alphaType(); SkAssertResult(SkColorTypeValidateAlphaType(info.colorType(), info.alphaType(), &newAlphaType)); return info.makeAlphaType(newAlphaType); }
/* * Performs the bitmap decoding for RLE input format * RLE decoding is performed all at once, rather than a one row at a time */ SkCodec::Result SkBmpRLECodec::decode(const SkImageInfo& dstInfo, void* dst, size_t dstRowBytes, const Options& opts) { // Set RLE flags static const uint8_t RLE_ESCAPE = 0; static const uint8_t RLE_EOL = 0; static const uint8_t RLE_EOF = 1; static const uint8_t RLE_DELTA = 2; // Set constant values const int width = dstInfo.width(); const int height = dstInfo.height(); // Destination parameters int x = 0; int y = 0; // Set the background as transparent. Then, if the RLE code skips pixels, // the skipped pixels will be transparent. // Because of the need for transparent pixels, kN32 is the only color // type that makes sense for the destination format. SkASSERT(kN32_SkColorType == dstInfo.colorType()); if (kNo_ZeroInitialized == opts.fZeroInitialized) { SkSwizzler::Fill(dst, dstInfo, dstRowBytes, height, SK_ColorTRANSPARENT, NULL); } while (true) { // If we have reached a row that is beyond the requested height, we have // succeeded. if (y >= height) { // It would be better to check for the EOF marker before returning // success, but we may be performing a scanline decode, which // may require us to stop before decoding the full height. return kSuccess; } // Every entry takes at least two bytes if ((int) fRLEBytes - fCurrRLEByte < 2) { SkCodecPrintf("Warning: might be incomplete RLE input.\n"); if (this->checkForMoreData() < 2) { return kIncompleteInput; } } // Read the next two bytes. These bytes have different meanings // depending on their values. In the first interpretation, the first // byte is an escape flag and the second byte indicates what special // task to perform. const uint8_t flag = fStreamBuffer.get()[fCurrRLEByte++]; const uint8_t task = fStreamBuffer.get()[fCurrRLEByte++]; // Perform decoding if (RLE_ESCAPE == flag) { switch (task) { case RLE_EOL: x = 0; y++; break; case RLE_EOF: return kSuccess; case RLE_DELTA: { // Two bytes are needed to specify delta if ((int) fRLEBytes - fCurrRLEByte < 2) { SkCodecPrintf("Warning: might be incomplete RLE input.\n"); if (this->checkForMoreData() < 2) { return kIncompleteInput; } } // Modify x and y const uint8_t dx = fStreamBuffer.get()[fCurrRLEByte++]; const uint8_t dy = fStreamBuffer.get()[fCurrRLEByte++]; x += dx; y += dy; if (x > width || y > height) { SkCodecPrintf("Warning: invalid RLE input.\n"); return kInvalidInput; } break; } default: { // If task does not match any of the above signals, it // indicates that we have a sequence of non-RLE pixels. // Furthermore, the value of task is equal to the number // of pixels to interpret. uint8_t numPixels = task; const size_t rowBytes = compute_row_bytes(numPixels, this->bitsPerPixel()); // Abort if setting numPixels moves us off the edge of the // image. if (x + numPixels > width) { SkCodecPrintf("Warning: invalid RLE input.\n"); return kInvalidInput; } // Also abort if there are not enough bytes // remaining in the stream to set numPixels. if ((int) fRLEBytes - fCurrRLEByte < SkAlign2(rowBytes)) { SkCodecPrintf("Warning: might be incomplete RLE input.\n"); if (this->checkForMoreData() < SkAlign2(rowBytes)) { return kIncompleteInput; } } // Set numPixels number of pixels while (numPixels > 0) { switch(this->bitsPerPixel()) { case 4: { SkASSERT(fCurrRLEByte < fRLEBytes); uint8_t val = fStreamBuffer.get()[fCurrRLEByte++]; setPixel(dst, dstRowBytes, dstInfo, x++, y, val >> 4); numPixels--; if (numPixels != 0) { setPixel(dst, dstRowBytes, dstInfo, x++, y, val & 0xF); numPixels--; } break; } case 8: SkASSERT(fCurrRLEByte < fRLEBytes); setPixel(dst, dstRowBytes, dstInfo, x++, y, fStreamBuffer.get()[fCurrRLEByte++]); numPixels--; break; case 24: { SkASSERT(fCurrRLEByte + 2 < fRLEBytes); uint8_t blue = fStreamBuffer.get()[fCurrRLEByte++]; uint8_t green = fStreamBuffer.get()[fCurrRLEByte++]; uint8_t red = fStreamBuffer.get()[fCurrRLEByte++]; setRGBPixel(dst, dstRowBytes, dstInfo, x++, y, red, green, blue); numPixels--; } default: SkASSERT(false); return kInvalidInput; } } // Skip a byte if necessary to maintain alignment if (!SkIsAlign2(rowBytes)) { fCurrRLEByte++; } break; } } } else { // If the first byte read is not a flag, it indicates the number of // pixels to set in RLE mode. const uint8_t numPixels = flag; const int endX = SkTMin<int>(x + numPixels, width); if (24 == this->bitsPerPixel()) { // In RLE24, the second byte read is part of the pixel color. // There are two more required bytes to finish encoding the // color. if ((int) fRLEBytes - fCurrRLEByte < 2) { SkCodecPrintf("Warning: might be incomplete RLE input.\n"); if (this->checkForMoreData() < 2) { return kIncompleteInput; } } // Fill the pixels up to endX with the specified color uint8_t blue = task; uint8_t green = fStreamBuffer.get()[fCurrRLEByte++]; uint8_t red = fStreamBuffer.get()[fCurrRLEByte++]; while (x < endX) { setRGBPixel(dst, dstRowBytes, dstInfo, x++, y, red, green, blue); } } else { // In RLE8 or RLE4, the second byte read gives the index in the // color table to look up the pixel color. // RLE8 has one color index that gets repeated // RLE4 has two color indexes in the upper and lower 4 bits of // the bytes, which are alternated uint8_t indices[2] = { task, task }; if (4 == this->bitsPerPixel()) { indices[0] >>= 4; indices[1] &= 0xf; } // Set the indicated number of pixels for (int which = 0; x < endX; x++) { setPixel(dst, dstRowBytes, dstInfo, x, y, indices[which]); which = !which; } } }
bool SkImageFilter::applyCropRect(const Context& ctx, Proxy* proxy, const SkBitmap& src, SkIPoint* srcOffset, SkIRect* bounds, SkBitmap* dst) const { SkIRect srcBounds; src.getBounds(&srcBounds); srcBounds.offset(*srcOffset); #ifdef SK_SUPPORT_SRC_BOUNDS_BLOAT_FOR_IMAGEFILTERS if (!fCropRect.applyTo(srcBounds, ctx, bounds)) { #else SkIRect dstBounds; this->onFilterNodeBounds(srcBounds, ctx.ctm(), &dstBounds, kForward_MapDirection); if (!fCropRect.applyTo(dstBounds, ctx, bounds)) { #endif return false; } if (srcBounds.contains(*bounds)) { *dst = src; return true; } else { SkAutoTUnref<SkBaseDevice> device(proxy->createDevice(bounds->width(), bounds->height())); if (!device) { return false; } SkCanvas canvas(device); canvas.clear(0x00000000); canvas.drawBitmap(src, srcOffset->x() - bounds->x(), srcOffset->y() - bounds->y()); *srcOffset = SkIPoint::Make(bounds->x(), bounds->y()); *dst = device->accessBitmap(false); return true; } } bool SkImageFilter::onFilterBounds(const SkIRect& src, const SkMatrix& ctm, SkIRect* dst) const { if (fInputCount < 1) { *dst = src; return true; } SkIRect bounds, totalBounds; this->onFilterNodeBounds(src, ctm, &bounds, kReverse_MapDirection); for (int i = 0; i < fInputCount; ++i) { SkImageFilter* filter = this->getInput(i); SkIRect rect = bounds; if (filter && !filter->filterBounds(bounds, ctm, &rect)) { return false; } if (0 == i) { totalBounds = rect; } else { totalBounds.join(rect); } } // don't modify dst until now, so we don't accidentally change it in the // loop, but then return false on the next filter. *dst = totalBounds; return true; } void SkImageFilter::onFilterNodeBounds(const SkIRect& src, const SkMatrix&, SkIRect* dst, MapDirection) const { *dst = src; } SkImageFilter::Context SkImageFilter::mapContext(const Context& ctx) const { #ifdef SK_SUPPORT_SRC_BOUNDS_BLOAT_FOR_IMAGEFILTERS return ctx; #else SkIRect clipBounds; this->onFilterNodeBounds(ctx.clipBounds(), ctx.ctm(), &clipBounds, MapDirection::kReverse_MapDirection); return Context(ctx.ctm(), clipBounds, ctx.cache()); #endif } bool SkImageFilter::asFragmentProcessor(GrFragmentProcessor**, GrTexture*, const SkMatrix&, const SkIRect&) const { return false; } SkImageFilter* SkImageFilter::CreateMatrixFilter(const SkMatrix& matrix, SkFilterQuality filterQuality, SkImageFilter* input) { return SkMatrixImageFilter::Create(matrix, filterQuality, input); } SkImageFilter* SkImageFilter::newWithLocalMatrix(const SkMatrix& matrix) const { // SkLocalMatrixImageFilter takes SkImage* in its factory, but logically that parameter // is *always* treated as a const ptr. Hence the const-cast here. // return SkLocalMatrixImageFilter::Create(matrix, const_cast<SkImageFilter*>(this)); } #if SK_SUPPORT_GPU bool SkImageFilter::filterInputGPU(int index, SkImageFilter::Proxy* proxy, const SkBitmap& src, const Context& ctx, SkBitmap* result, SkIPoint* offset) const { SkImageFilter* input = this->getInput(index); if (!input) { return true; } // Ensure that GrContext calls under filterImage and filterImageGPU below will see an identity // matrix with no clip and that the matrix, clip, and render target set before this function was // called are restored before we return to the caller. GrContext* context = src.getTexture()->getContext(); if (input->filterImage(proxy, src, this->mapContext(ctx), result, offset)) { if (!result->getTexture()) { const SkImageInfo info = result->info(); if (kUnknown_SkColorType == info.colorType()) { return false; } SkAutoTUnref<GrTexture> resultTex( GrRefCachedBitmapTexture(context, *result, GrTextureParams::ClampNoFilter())); if (!resultTex) { return false; } result->setPixelRef(new SkGrPixelRef(info, resultTex))->unref(); } return true; } else { return false; } }
SkImageCacherator::CachedFormat SkImage_Lazy::chooseCacheFormat(SkColorSpace* dstColorSpace, const GrCaps* grCaps) const { SkColorSpace* cs = fInfo.colorSpace(); if (!cs || !dstColorSpace) { return kLegacy_CachedFormat; } CacheCaps caps(grCaps); switch (fInfo.colorType()) { case kUnknown_SkColorType: case kAlpha_8_SkColorType: case kRGB_565_SkColorType: case kARGB_4444_SkColorType: case kRGB_888x_SkColorType: case kRGBA_1010102_SkColorType: case kRGB_101010x_SkColorType: // We don't support color space on these formats, so always decode in legacy mode: // TODO: Ask the codec to decode these to something else (at least sRGB 8888)? return kLegacy_CachedFormat; case kGray_8_SkColorType: // TODO: What do we do with grayscale sources that have strange color spaces attached? // The codecs and color space xform don't handle this correctly (yet), so drop it on // the floor. (Also, inflating by a factor of 8 is going to be unfortunate). // As it is, we don't directly support sRGB grayscale, so ask the codec to convert // it for us. This bypasses some really sketchy code GrUploadPixmapToTexture. if (cs->gammaCloseToSRGB() && caps.supportsSRGB()) { return kSRGB8888_CachedFormat; } else { return kLegacy_CachedFormat; } case kRGBA_8888_SkColorType: if (cs->gammaCloseToSRGB()) { if (caps.supportsSRGB()) { return kSRGB8888_CachedFormat; } else if (caps.supportsHalfFloat()) { return kLinearF16_CachedFormat; } else { return kLegacy_CachedFormat; } } else { if (caps.supportsHalfFloat()) { return kLinearF16_CachedFormat; } else if (caps.supportsSRGB()) { return kSRGB8888_CachedFormat; } else { return kLegacy_CachedFormat; } } case kBGRA_8888_SkColorType: // Odd case. sBGRA isn't a real thing, so we may not have this texturable. if (caps.supportsSBGR()) { if (cs->gammaCloseToSRGB()) { return kSBGR8888_CachedFormat; } else if (caps.supportsHalfFloat()) { return kLinearF16_CachedFormat; } else if (caps.supportsSRGB()) { return kSRGB8888_CachedFormat; } else { // sBGRA support without sRGBA is highly unlikely (impossible?) Nevertheless. return kLegacy_CachedFormat; } } else { if (cs->gammaCloseToSRGB()) { if (caps.supportsSRGB()) { return kSRGB8888_CachedFormat; } else if (caps.supportsHalfFloat()) { return kLinearF16_CachedFormat; } else { return kLegacy_CachedFormat; } } else { if (caps.supportsHalfFloat()) { return kLinearF16_CachedFormat; } else if (caps.supportsSRGB()) { return kSRGB8888_CachedFormat; } else { return kLegacy_CachedFormat; } } } case kRGBA_F16_SkColorType: if (caps.supportsHalfFloat()) { return kLinearF16_CachedFormat; } else if (caps.supportsSRGB()) { return kSRGB8888_CachedFormat; } else { return kLegacy_CachedFormat; } } SkDEBUGFAIL("Unreachable"); return kLegacy_CachedFormat; }
size_t SkImage::getDeferredTextureImageData(const GrContextThreadSafeProxy& proxy, const DeferredTextureImageUsageParams params[], int paramCnt, void* buffer, SkColorSpace* dstColorSpace) const { // Extract relevant min/max values from the params array. int lowestPreScaleMipLevel = params[0].fPreScaleMipLevel; SkFilterQuality highestFilterQuality = params[0].fQuality; bool useMipMaps = should_use_mip_maps(params[0]); for (int i = 1; i < paramCnt; ++i) { if (lowestPreScaleMipLevel > params[i].fPreScaleMipLevel) lowestPreScaleMipLevel = params[i].fPreScaleMipLevel; if (highestFilterQuality < params[i].fQuality) highestFilterQuality = params[i].fQuality; useMipMaps |= should_use_mip_maps(params[i]); } const bool fillMode = SkToBool(buffer); if (fillMode && !SkIsAlign8(reinterpret_cast<intptr_t>(buffer))) { return 0; } // Calculate scaling parameters. bool isScaled = lowestPreScaleMipLevel != 0; SkISize scaledSize; if (isScaled) { // SkMipMap::ComputeLevelSize takes an index into an SkMipMap. SkMipMaps don't contain the // base level, so to get an SkMipMap index we must subtract one from the GL MipMap level. scaledSize = SkMipMap::ComputeLevelSize(this->width(), this->height(), lowestPreScaleMipLevel - 1); } else { scaledSize = SkISize::Make(this->width(), this->height()); } // We never want to scale at higher than SW medium quality, as SW medium matches GPU high. SkFilterQuality scaleFilterQuality = highestFilterQuality; if (scaleFilterQuality > kMedium_SkFilterQuality) { scaleFilterQuality = kMedium_SkFilterQuality; } const int maxTextureSize = proxy.fCaps->maxTextureSize(); if (scaledSize.width() > maxTextureSize || scaledSize.height() > maxTextureSize) { return 0; } SkAutoPixmapStorage pixmap; SkImageInfo info; size_t pixelSize = 0; size_t ctSize = 0; int ctCount = 0; if (!isScaled && this->peekPixels(&pixmap)) { info = pixmap.info(); pixelSize = SkAlign8(pixmap.getSafeSize()); if (pixmap.ctable()) { ctCount = pixmap.ctable()->count(); ctSize = SkAlign8(pixmap.ctable()->count() * 4); } } else { // Here we're just using presence of data to know whether there is a codec behind the image. // In the future we will access the cacherator and get the exact data that we want to (e.g. // yuv planes) upload. sk_sp<SkData> data(this->refEncoded()); if (!data && !this->peekPixels(nullptr)) { return 0; } info = as_IB(this)->onImageInfo().makeWH(scaledSize.width(), scaledSize.height()); pixelSize = SkAlign8(SkAutoPixmapStorage::AllocSize(info, nullptr)); if (fillMode) { pixmap.alloc(info); if (isScaled) { if (!this->scalePixels(pixmap, scaleFilterQuality, SkImage::kDisallow_CachingHint)) { return 0; } } else { if (!this->readPixels(pixmap, 0, 0, SkImage::kDisallow_CachingHint)) { return 0; } } SkASSERT(!pixmap.ctable()); } } int mipMapLevelCount = 1; if (useMipMaps) { // SkMipMap only deals with the mipmap levels it generates, which does // not include the base level. // That means it generates and holds levels 1-x instead of 0-x. // So the total mipmap level count is 1 more than what // SkMipMap::ComputeLevelCount returns. mipMapLevelCount = SkMipMap::ComputeLevelCount(scaledSize.width(), scaledSize.height()) + 1; // We already initialized pixelSize to the size of the base level. // SkMipMap will generate the extra mipmap levels. Their sizes need to // be added to the total. // Index 0 here does not refer to the base mipmap level -- it is // SkMipMap's first generated mipmap level (level 1). for (int currentMipMapLevelIndex = mipMapLevelCount - 2; currentMipMapLevelIndex >= 0; currentMipMapLevelIndex--) { SkISize mipSize = SkMipMap::ComputeLevelSize(scaledSize.width(), scaledSize.height(), currentMipMapLevelIndex); SkImageInfo mipInfo = info.makeWH(mipSize.fWidth, mipSize.fHeight); pixelSize += SkAlign8(SkAutoPixmapStorage::AllocSize(mipInfo, nullptr)); } } size_t size = 0; size_t dtiSize = SkAlign8(sizeof(DeferredTextureImage)); size += dtiSize; size += (mipMapLevelCount - 1) * sizeof(MipMapLevelData); // We subtract 1 because DeferredTextureImage already includes the base // level in its size size_t pixelOffset = size; size += pixelSize; size_t ctOffset = size; size += ctSize; size_t colorSpaceOffset = 0; size_t colorSpaceSize = 0; if (info.colorSpace()) { colorSpaceOffset = size; colorSpaceSize = info.colorSpace()->writeToMemory(nullptr); size += colorSpaceSize; } if (!fillMode) { return size; } char* bufferAsCharPtr = reinterpret_cast<char*>(buffer); char* pixelsAsCharPtr = bufferAsCharPtr + pixelOffset; void* pixels = pixelsAsCharPtr; void* ct = nullptr; if (ctSize) { ct = bufferAsCharPtr + ctOffset; } memcpy(reinterpret_cast<void*>(SkAlign8(reinterpret_cast<uintptr_t>(pixelsAsCharPtr))), pixmap.addr(), pixmap.getSafeSize()); if (ctSize) { memcpy(ct, pixmap.ctable()->readColors(), ctSize); } // If the context has sRGB support, and we're intending to render to a surface with an attached // color space, and the image has an sRGB-like color space attached, then use our gamma (sRGB) // aware mip-mapping. SkSourceGammaTreatment gammaTreatment = SkSourceGammaTreatment::kIgnore; if (proxy.fCaps->srgbSupport() && SkToBool(dstColorSpace) && info.colorSpace() && info.colorSpace()->gammaCloseToSRGB()) { gammaTreatment = SkSourceGammaTreatment::kRespect; } SkASSERT(info == pixmap.info()); size_t rowBytes = pixmap.rowBytes(); static_assert(std::is_standard_layout<DeferredTextureImage>::value, "offsetof, which we use below, requires the type have standard layout"); auto dtiBufferFiller = DTIBufferFiller{bufferAsCharPtr}; FILL_MEMBER(dtiBufferFiller, fGammaTreatment, &gammaTreatment); FILL_MEMBER(dtiBufferFiller, fContextUniqueID, &proxy.fContextUniqueID); int width = info.width(); FILL_MEMBER(dtiBufferFiller, fWidth, &width); int height = info.height(); FILL_MEMBER(dtiBufferFiller, fHeight, &height); SkColorType colorType = info.colorType(); FILL_MEMBER(dtiBufferFiller, fColorType, &colorType); SkAlphaType alphaType = info.alphaType(); FILL_MEMBER(dtiBufferFiller, fAlphaType, &alphaType); FILL_MEMBER(dtiBufferFiller, fColorTableCnt, &ctCount); FILL_MEMBER(dtiBufferFiller, fColorTableData, &ct); FILL_MEMBER(dtiBufferFiller, fMipMapLevelCount, &mipMapLevelCount); memcpy(bufferAsCharPtr + offsetof(DeferredTextureImage, fMipMapLevelData[0].fPixelData), &pixels, sizeof(pixels)); memcpy(bufferAsCharPtr + offsetof(DeferredTextureImage, fMipMapLevelData[0].fRowBytes), &rowBytes, sizeof(rowBytes)); if (colorSpaceSize) { void* colorSpace = bufferAsCharPtr + colorSpaceOffset; FILL_MEMBER(dtiBufferFiller, fColorSpace, &colorSpace); FILL_MEMBER(dtiBufferFiller, fColorSpaceSize, &colorSpaceSize); info.colorSpace()->writeToMemory(bufferAsCharPtr + colorSpaceOffset); } else { memset(bufferAsCharPtr + offsetof(DeferredTextureImage, fColorSpace), 0, sizeof(DeferredTextureImage::fColorSpace)); memset(bufferAsCharPtr + offsetof(DeferredTextureImage, fColorSpaceSize), 0, sizeof(DeferredTextureImage::fColorSpaceSize)); } // Fill in the mipmap levels if they exist char* mipLevelPtr = pixelsAsCharPtr + SkAlign8(pixmap.getSafeSize()); if (useMipMaps) { static_assert(std::is_standard_layout<MipMapLevelData>::value, "offsetof, which we use below, requires the type have a standard layout"); SkAutoTDelete<SkMipMap> mipmaps(SkMipMap::Build(pixmap, gammaTreatment, nullptr)); // SkMipMap holds only the mipmap levels it generates. // A programmer can use the data they provided to SkMipMap::Build as level 0. // So the SkMipMap provides levels 1-x but it stores them in its own // range 0-(x-1). for (int generatedMipLevelIndex = 0; generatedMipLevelIndex < mipMapLevelCount - 1; generatedMipLevelIndex++) { SkMipMap::Level mipLevel; mipmaps->getLevel(generatedMipLevelIndex, &mipLevel); // Make sure the mipmap data is after the start of the buffer SkASSERT(mipLevelPtr > bufferAsCharPtr); // Make sure the mipmap data starts before the end of the buffer SkASSERT(mipLevelPtr < bufferAsCharPtr + pixelOffset + pixelSize); // Make sure the mipmap data ends before the end of the buffer SkASSERT(mipLevelPtr + mipLevel.fPixmap.getSafeSize() <= bufferAsCharPtr + pixelOffset + pixelSize); // getSafeSize includes rowbyte padding except for the last row, // right? memcpy(mipLevelPtr, mipLevel.fPixmap.addr(), mipLevel.fPixmap.getSafeSize()); memcpy(bufferAsCharPtr + offsetof(DeferredTextureImage, fMipMapLevelData) + sizeof(MipMapLevelData) * (generatedMipLevelIndex + 1) + offsetof(MipMapLevelData, fPixelData), &mipLevelPtr, sizeof(void*)); size_t rowBytes = mipLevel.fPixmap.rowBytes(); memcpy(bufferAsCharPtr + offsetof(DeferredTextureImage, fMipMapLevelData) + sizeof(MipMapLevelData) * (generatedMipLevelIndex + 1) + offsetof(MipMapLevelData, fRowBytes), &rowBytes, sizeof(rowBytes)); mipLevelPtr += SkAlign8(mipLevel.fPixmap.getSafeSize()); } } return size; }
bool allocHandle(const SkImageInfo& info, Rec* rec) override { SkASSERT(info.colorType() == kN32_SkColorType); return Create(info.width(), info.height(), info.isOpaque(), rec); }
DEF_GPUTEST_FOR_RENDERING_CONTEXTS(CopySurface, reporter, ctxInfo) { GrContext* context = ctxInfo.grContext(); static const int kW = 10; static const int kH = 10; static const size_t kRowBytes = sizeof(uint32_t) * kW; SkAutoTMalloc<uint32_t> srcPixels(kW * kH); for (int i = 0; i < kW * kH; ++i) { srcPixels.get()[i] = i; } SkAutoTMalloc<uint32_t> dstPixels(kW * kH); for (int i = 0; i < kW * kH; ++i) { dstPixels.get()[i] = ~i; } static const SkIRect kSrcRects[] { { 0, 0, kW , kH }, {-1, -1, kW+1, kH+1}, { 1, 1, kW-1, kH-1}, { 5, 5, 6 , 6 }, }; static const SkIPoint kDstPoints[] { { 0 , 0 }, { 1 , 1 }, { kW/2, kH/4}, { kW-1, kH-1}, { kW , kH }, { kW+1, kH+2}, {-1 , -1 }, }; const SkImageInfo ii = SkImageInfo::Make(kW, kH, kRGBA_8888_SkColorType, kPremul_SkAlphaType); SkAutoTMalloc<uint32_t> read(kW * kH); for (auto sOrigin : {kBottomLeft_GrSurfaceOrigin, kTopLeft_GrSurfaceOrigin}) { for (auto dOrigin : {kBottomLeft_GrSurfaceOrigin, kTopLeft_GrSurfaceOrigin}) { for (auto sRT : {true, false}) { for (auto dRT : {true, false}) { for (auto srcRect : kSrcRects) { for (auto dstPoint : kDstPoints) { auto src = sk_gpu_test::MakeTextureProxyFromData( context, sRT, kW, kH, ii.colorType(), sOrigin, srcPixels.get(), kRowBytes); auto dst = sk_gpu_test::MakeTextureProxyFromData( context, dRT, kW, kH, ii.colorType(), dOrigin, dstPixels.get(), kRowBytes); if (!src || !dst) { ERRORF(reporter, "Could not create surfaces for copy surface test."); continue; } sk_sp<GrSurfaceContext> dstContext = context->contextPriv().makeWrappedSurfaceContext(std::move(dst)); bool result = dstContext->copy(src.get(), srcRect, dstPoint); bool expectedResult = true; SkIPoint dstOffset = { dstPoint.fX - srcRect.fLeft, dstPoint.fY - srcRect.fTop }; SkIRect copiedDstRect = SkIRect::MakeXYWH(dstPoint.fX, dstPoint.fY, srcRect.width(), srcRect.height()); SkIRect copiedSrcRect; if (!copiedSrcRect.intersect(srcRect, SkIRect::MakeWH(kW, kH))) { expectedResult = false; } else { // If the src rect was clipped, apply same clipping to each side of // copied dst rect. copiedDstRect.fLeft += copiedSrcRect.fLeft - srcRect.fLeft; copiedDstRect.fTop += copiedSrcRect.fTop - srcRect.fTop; copiedDstRect.fRight -= copiedSrcRect.fRight - srcRect.fRight; copiedDstRect.fBottom -= copiedSrcRect.fBottom - srcRect.fBottom; } if (copiedDstRect.isEmpty() || !copiedDstRect.intersect(SkIRect::MakeWH(kW, kH))) { expectedResult = false; } // To make the copied src rect correct we would apply any dst clipping // back to the src rect, but we don't use it again so don't bother. if (expectedResult != result) { ERRORF(reporter, "Expected return value %d from copySurface, got " "%d.", expectedResult, result); continue; } if (!expectedResult || !result) { continue; } sk_memset32(read.get(), 0, kW * kH); if (!dstContext->readPixels(ii, read.get(), kRowBytes, 0, 0)) { ERRORF(reporter, "Error calling readPixels"); continue; } bool abort = false; // Validate that pixels inside copiedDstRect received the correct value // from src and that those outside were not modified. for (int y = 0; y < kH && !abort; ++y) { for (int x = 0; x < kW; ++x) { uint32_t r = read.get()[y * kW + x]; if (copiedDstRect.contains(x, y)) { int sx = x - dstOffset.fX; int sy = y - dstOffset.fY; uint32_t s = srcPixels.get()[sy * kW + sx]; if (s != r) { ERRORF(reporter, "Expected dst %d,%d to contain " "0x%08x copied from src location %d,%d. Got " "0x%08x", x, y, s, sx, sy, r); abort = true; break; } } else { uint32_t d = dstPixels.get()[y * kW + x]; if (d != r) { ERRORF(reporter, "Expected dst %d,%d to be unmodified (" "0x%08x). Got 0x%08x", x, y, d, r); abort = true; break; } } } } } } } } } } }
// Currently the raster imagefilters can only handle certain imageinfos. Call this to know if // a given info is supported. static bool valid_for_imagefilters(const SkImageInfo& info) { // no support for other swizzles/depths yet return info.colorType() == kN32_SkColorType; }
bool SkPixelInfo::CopyPixels(const SkImageInfo& dstInfo, void* dstPixels, size_t dstRB, const SkImageInfo& srcInfo, const void* srcPixels, size_t srcRB, SkColorTable* ctable) { if (srcInfo.dimensions() != dstInfo.dimensions()) { return false; } const int width = srcInfo.width(); const int height = srcInfo.height(); // Handle fancy alpha swizzling if both are ARGB32 if (4 == srcInfo.bytesPerPixel() && 4 == dstInfo.bytesPerPixel()) { SkDstPixelInfo dstPI; dstPI.fColorType = dstInfo.colorType(); dstPI.fAlphaType = dstInfo.alphaType(); dstPI.fPixels = dstPixels; dstPI.fRowBytes = dstRB; SkSrcPixelInfo srcPI; srcPI.fColorType = srcInfo.colorType(); srcPI.fAlphaType = srcInfo.alphaType(); srcPI.fPixels = srcPixels; srcPI.fRowBytes = srcRB; return srcPI.convertPixelsTo(&dstPI, width, height); } // If they agree on colorType and the alphaTypes are compatible, then we just memcpy. // Note: we've already taken care of 32bit colortypes above. if (srcInfo.colorType() == dstInfo.colorType()) { switch (srcInfo.colorType()) { case kRGB_565_SkColorType: case kAlpha_8_SkColorType: break; case kIndex_8_SkColorType: case kARGB_4444_SkColorType: if (srcInfo.alphaType() != dstInfo.alphaType()) { return false; } break; default: return false; } rect_memcpy(dstPixels, dstRB, srcPixels, srcRB, width * srcInfo.bytesPerPixel(), height); return true; } /* * Begin section where we try to change colorTypes along the way. Not all combinations * are supported. */ // Can no longer draw directly into 4444, but we can manually whack it for a few combinations if (kARGB_4444_SkColorType == dstInfo.colorType() && (kN32_SkColorType == srcInfo.colorType() || kIndex_8_SkColorType == srcInfo.colorType())) { if (srcInfo.alphaType() == kUnpremul_SkAlphaType) { // Our method for converting to 4444 assumes premultiplied. return false; } const SkPMColor* table = NULL; if (kIndex_8_SkColorType == srcInfo.colorType()) { if (NULL == ctable) { return false; } table = ctable->readColors(); } for (int y = 0; y < height; ++y) { DITHER_4444_SCAN(y); SkPMColor16* SK_RESTRICT dstRow = (SkPMColor16*)dstPixels; if (table) { const uint8_t* SK_RESTRICT srcRow = (const uint8_t*)srcPixels; for (int x = 0; x < width; ++x) { dstRow[x] = SkDitherARGB32To4444(table[srcRow[x]], DITHER_VALUE(x)); } } else { const SkPMColor* SK_RESTRICT srcRow = (const SkPMColor*)srcPixels; for (int x = 0; x < width; ++x) { dstRow[x] = SkDitherARGB32To4444(srcRow[x], DITHER_VALUE(x)); } } dstPixels = (char*)dstPixels + dstRB; srcPixels = (const char*)srcPixels + srcRB; } return true; }
SkCodec::Result SkCodec::getPixels(const SkImageInfo& info, void* pixels, size_t rowBytes, const Options* options, SkPMColor ctable[], int* ctableCount) { if (kUnknown_SkColorType == info.colorType()) { return kInvalidConversion; } if (nullptr == pixels) { return kInvalidParameters; } if (rowBytes < info.minRowBytes()) { return kInvalidParameters; } if (kIndex_8_SkColorType == info.colorType()) { if (nullptr == ctable || nullptr == ctableCount) { return kInvalidParameters; } } else { if (ctableCount) { *ctableCount = 0; } ctableCount = nullptr; ctable = nullptr; } { SkAlphaType canonical; if (!SkColorTypeValidateAlphaType(info.colorType(), info.alphaType(), &canonical) || canonical != info.alphaType()) { return kInvalidConversion; } } if (!this->rewindIfNeeded()) { return kCouldNotRewind; } // Default options. Options optsStorage; if (nullptr == options) { options = &optsStorage; } else if (options->fSubset) { SkIRect subset(*options->fSubset); if (!this->onGetValidSubset(&subset) || subset != *options->fSubset) { // FIXME: How to differentiate between not supporting subset at all // and not supporting this particular subset? return kUnimplemented; } } // FIXME: Support subsets somehow? Note that this works for SkWebpCodec // because it supports arbitrary scaling/subset combinations. if (!this->dimensionsSupported(info.dimensions())) { return kInvalidScale; } // On an incomplete decode, the subclass will specify the number of scanlines that it decoded // successfully. int rowsDecoded = 0; const Result result = this->onGetPixels(info, pixels, rowBytes, *options, ctable, ctableCount, &rowsDecoded); if ((kIncompleteInput == result || kSuccess == result) && ctableCount) { SkASSERT(*ctableCount >= 0 && *ctableCount <= 256); } // A return value of kIncompleteInput indicates a truncated image stream. // In this case, we will fill any uninitialized memory with a default value. // Some subclasses will take care of filling any uninitialized memory on // their own. They indicate that all of the memory has been filled by // setting rowsDecoded equal to the height. if (kIncompleteInput == result && rowsDecoded != info.height()) { this->fillIncompleteImage(info, pixels, rowBytes, options->fZeroInitialized, info.height(), rowsDecoded); } return result; }
/* * Checks if the conversion between the input image and the requested output * image has been implemented * Sets the output color space */ bool SkJpegCodec::setOutputColorSpace(const SkImageInfo& dst) { const SkImageInfo& src = this->getInfo(); // Ensure that the profile type is unchanged if (dst.profileType() != src.profileType()) { return false; } if (kUnknown_SkAlphaType == dst.alphaType()) { return false; } if (kOpaque_SkAlphaType != dst.alphaType()) { SkCodecPrintf("Warning: an opaque image should be decoded as opaque " "- it is being decoded as non-opaque, which will draw slower\n"); } // Check if we will decode to CMYK because a conversion to RGBA is not supported J_COLOR_SPACE colorSpace = fDecoderMgr->dinfo()->jpeg_color_space; bool isCMYK = JCS_CMYK == colorSpace || JCS_YCCK == colorSpace; // Check for valid color types and set the output color space switch (dst.colorType()) { case kN32_SkColorType: if (isCMYK) { fDecoderMgr->dinfo()->out_color_space = JCS_CMYK; } else { #ifdef LIBJPEG_TURBO_VERSION // Check the byte ordering of the RGBA color space for the // current platform #ifdef SK_PMCOLOR_IS_RGBA fDecoderMgr->dinfo()->out_color_space = JCS_EXT_RGBA; #else fDecoderMgr->dinfo()->out_color_space = JCS_EXT_BGRA; #endif #else fDecoderMgr->dinfo()->out_color_space = JCS_RGB; #endif } return true; case kRGB_565_SkColorType: if (isCMYK) { fDecoderMgr->dinfo()->out_color_space = JCS_CMYK; } else { #ifdef TURBO_HAS_565 fDecoderMgr->dinfo()->dither_mode = JDITHER_NONE; fDecoderMgr->dinfo()->out_color_space = JCS_RGB565; #else fDecoderMgr->dinfo()->out_color_space = JCS_RGB; #endif } return true; case kGray_8_SkColorType: if (isCMYK) { return false; } else { // We will enable decodes to gray even if the image is color because this is // much faster than decoding to color and then converting fDecoderMgr->dinfo()->out_color_space = JCS_GRAYSCALE; } return true; default: return false; } }