bool SkPixmap::readPixels(const SkImageInfo& requestedDstInfo, void* dstPixels, size_t dstRB, int x, int y) const { if (kUnknown_SkColorType == requestedDstInfo.colorType()) { return false; } if (nullptr == dstPixels || dstRB < requestedDstInfo.minRowBytes()) { return false; } if (0 == requestedDstInfo.width() || 0 == requestedDstInfo.height()) { return false; } SkIRect srcR = SkIRect::MakeXYWH(x, y, requestedDstInfo.width(), requestedDstInfo.height()); if (!srcR.intersect(0, 0, this->width(), this->height())) { return false; } // the intersect may have shrunk info's logical size const SkImageInfo dstInfo = requestedDstInfo.makeWH(srcR.width(), srcR.height()); // if x or y are negative, then we have to adjust pixels if (x > 0) { x = 0; } if (y > 0) { y = 0; } // here x,y are either 0 or negative dstPixels = ((char*)dstPixels - y * dstRB - x * dstInfo.bytesPerPixel()); const SkImageInfo srcInfo = this->info().makeWH(dstInfo.width(), dstInfo.height()); const void* srcPixels = this->addr(srcR.x(), srcR.y()); return SkPixelInfo::CopyPixels(dstInfo, dstPixels, dstRB, srcInfo, srcPixels, this->rowBytes(), this->ctable()); }
static void assert_equal(skiatest::Reporter* reporter, SkImage* a, const SkIRect* subsetA, SkImage* b) { const int widthA = subsetA ? subsetA->width() : a->width(); const int heightA = subsetA ? subsetA->height() : a->height(); REPORTER_ASSERT(reporter, widthA == b->width()); REPORTER_ASSERT(reporter, heightA == b->height()); #if 0 // see skbug.com/3965 bool AO = a->isOpaque(); bool BO = b->isOpaque(); REPORTER_ASSERT(reporter, AO == BO); #endif SkImageInfo info = SkImageInfo::MakeN32(widthA, heightA, a->isOpaque() ? kOpaque_SkAlphaType : kPremul_SkAlphaType); SkAutoPixmapStorage pmapA, pmapB; pmapA.alloc(info); pmapB.alloc(info); const int srcX = subsetA ? subsetA->x() : 0; const int srcY = subsetA ? subsetA->y() : 0; REPORTER_ASSERT(reporter, a->readPixels(pmapA, srcX, srcY)); REPORTER_ASSERT(reporter, b->readPixels(pmapB, 0, 0)); const size_t widthBytes = widthA * info.bytesPerPixel(); for (int y = 0; y < heightA; ++y) { REPORTER_ASSERT(reporter, !memcmp(pmapA.addr32(0, y), pmapB.addr32(0, y), widthBytes)); } }
// If we already have a stored, can we reuse it instead of also storing b? static bool equivalent(const SkBitmap& a, const SkBitmap& b) { if (a.info() != b.info() || a.pixelRefOrigin() != b.pixelRefOrigin()) { // Requiring a.info() == b.info() may be overkill in some cases (alphatype mismatch), // but it sure makes things easier to reason about below. return false; } if (a.pixelRef() == b.pixelRef()) { return true; // Same shape and same pixels -> same bitmap. } // From here down we're going to have to look at the bitmap data, so we require pixelRefs(). if (!a.pixelRef() || !b.pixelRef()) { return false; } // If the bitmaps have encoded data, check first before locking pixels so they don't decode. SkAutoTUnref<SkData> encA(a.pixelRef()->refEncodedData()), encB(b.pixelRef()->refEncodedData()); if (encA && encB) { return encA->equals(encB); } else if (encA || encB) { return false; // One has encoded data but the other does not. } // As a last resort, we have to look at the pixels. This will read back textures. SkAutoLockPixels al(a), bl(b); const char* ap = (const char*)a.getPixels(); const char* bp = (const char*)b.getPixels(); if (ap && bp) { // We check row by row; row bytes might differ. SkASSERT(a.info() == b.info()); // We checked this above. SkASSERT(a.info().bytesPerPixel() > 0); // If we have pixelRefs, this better be true. const SkImageInfo info = a.info(); const size_t bytesToCompare = info.width() * info.bytesPerPixel(); for (int row = 0; row < info.height(); row++) { if (0 != memcmp(ap, bp, bytesToCompare)) { return false; } ap += a.rowBytes(); bp += b.rowBytes(); } return true; } return false; // Couldn't get pixels for both bitmaps. }
static bool choose_linear_pipeline(const SkShader::ContextRec& rec, const SkImageInfo& srcInfo) { // These src attributes are not supported in the new 4f context (yet) // if (srcInfo.colorType() != kRGBA_8888_SkColorType && srcInfo.colorType() != kBGRA_8888_SkColorType && srcInfo.colorType() != kIndex_8_SkColorType) { return false; } #if 0 // later we may opt-in to the new code even if the client hasn't requested it... // These src attributes are only supported in the new 4f context // if (srcInfo.isSRGB() || kUnpremul_SkAlphaType == srcInfo.alphaType() || (4 == srcInfo.bytesPerPixel() && kN32_SkColorType != srcInfo.colorType())) { return true; } #endif // If we get here, we can reasonably use either context, respect the caller's preference // return SkShader::ContextRec::kPM4f_DstType == rec.fPreferredDstType; }
void SubsetSingleBench::onDraw(const int n, SkCanvas* canvas) { // When the color type is kIndex8, we will need to store the color table. If it is // used, it will be initialized by the codec. int colorCount; SkPMColor colors[256]; if (fUseCodec) { for (int count = 0; count < n; count++) { SkAutoTDelete<SkCodec> codec(SkCodec::NewFromStream(fStream->duplicate())); const SkImageInfo info = codec->getInfo().makeColorType(fColorType); SkAutoTDeleteArray<uint8_t> row(SkNEW_ARRAY(uint8_t, info.minRowBytes())); SkScanlineDecoder* scanlineDecoder = codec->getScanlineDecoder( info, NULL, colors, &colorCount); SkBitmap bitmap; bitmap.allocPixels(info.makeWH(fSubsetWidth, fSubsetHeight)); scanlineDecoder->skipScanlines(fOffsetTop); uint32_t bpp = info.bytesPerPixel(); for (uint32_t y = 0; y < fSubsetHeight; y++) { scanlineDecoder->getScanlines(row.get(), 1, 0); memcpy(bitmap.getAddr(0, y), row.get() + fOffsetLeft * bpp, fSubsetWidth * bpp); } } } else { for (int count = 0; count < n; count++) { SkAutoTDelete<SkImageDecoder> decoder(SkImageDecoder::Factory(fStream)); int width, height; decoder->buildTileIndex(fStream->duplicate(), &width, &height); SkBitmap bitmap; SkIRect rect = SkIRect::MakeXYWH(fOffsetLeft, fOffsetTop, fSubsetWidth, fSubsetHeight); decoder->decodeSubset(&bitmap, rect, fColorType); } } }
void SubsetZoomBench::onDraw(const int n, SkCanvas* canvas) { // When the color type is kIndex8, we will need to store the color table. If it is // used, it will be initialized by the codec. int colorCount; SkPMColor colors[256]; if (fUseCodec) { for (int count = 0; count < n; count++) { SkAutoTDelete<SkScanlineDecoder> scanlineDecoder( SkScanlineDecoder::NewFromStream(fStream->duplicate())); const SkImageInfo info = scanlineDecoder->getInfo().makeColorType(fColorType); SkAutoTDeleteArray<uint8_t> row(new uint8_t[info.minRowBytes()]); scanlineDecoder->start(info, nullptr, colors, &colorCount); const int centerX = info.width() / 2; const int centerY = info.height() / 2; int w = fSubsetWidth; int h = fSubsetHeight; do { const int subsetStartX = SkTMax(0, centerX - w / 2); const int subsetStartY = SkTMax(0, centerY - h / 2); const int subsetWidth = SkTMin(w, info.width() - subsetStartX); const int subsetHeight = SkTMin(h, info.height() - subsetStartY); // Note that if we subsetted and scaled in a single step, we could use the // same bitmap - as is often done in actual use cases. SkBitmap bitmap; SkImageInfo subsetInfo = info.makeWH(subsetWidth, subsetHeight); alloc_pixels(&bitmap, subsetInfo, colors, colorCount); uint32_t bpp = info.bytesPerPixel(); scanlineDecoder->skipScanlines(subsetStartY); for (int y = 0; y < subsetHeight; y++) { scanlineDecoder->getScanlines(row.get(), 1, 0); memcpy(bitmap.getAddr(0, y), row.get() + subsetStartX * bpp, subsetWidth * bpp); } w <<= 1; h <<= 1; } while (w < 2 * info.width() || h < 2 * info.height()); } } else { for (int count = 0; count < n; count++) { int width, height; SkAutoTDelete<SkImageDecoder> decoder(SkImageDecoder::Factory(fStream)); decoder->buildTileIndex(fStream->duplicate(), &width, &height); const int centerX = width / 2; const int centerY = height / 2; int w = fSubsetWidth; int h = fSubsetHeight; do { const int subsetStartX = SkTMax(0, centerX - w / 2); const int subsetStartY = SkTMax(0, centerY - h / 2); const int subsetWidth = SkTMin(w, width - subsetStartX); const int subsetHeight = SkTMin(h, height - subsetStartY); SkBitmap bitmap; SkIRect rect = SkIRect::MakeXYWH(subsetStartX, subsetStartY, subsetWidth, subsetHeight); decoder->decodeSubset(&bitmap, rect, fColorType); w <<= 1; h <<= 1; } while (w < 2 * width || h < 2 * height); } } }
void SubsetTranslateBench::onDraw(const int n, SkCanvas* canvas) { // When the color type is kIndex8, we will need to store the color table. If it is // used, it will be initialized by the codec. int colorCount; SkPMColor colors[256]; if (fUseCodec) { for (int count = 0; count < n; count++) { SkAutoTDelete<SkScanlineDecoder> scanlineDecoder( SkScanlineDecoder::NewFromStream(fStream->duplicate())); const SkImageInfo info = scanlineDecoder->getInfo().makeColorType(fColorType); SkAutoTDeleteArray<uint8_t> row(new uint8_t[info.minRowBytes()]); scanlineDecoder->start(info, nullptr, colors, &colorCount); SkBitmap bitmap; // Note that we use the same bitmap for all of the subsets. // It might be larger than necessary for the end subsets. SkImageInfo subsetInfo = info.makeWH(fSubsetWidth, fSubsetHeight); alloc_pixels(&bitmap, subsetInfo, colors, colorCount); for (int x = 0; x < info.width(); x += fSubsetWidth) { for (int y = 0; y < info.height(); y += fSubsetHeight) { scanlineDecoder->skipScanlines(y); const uint32_t currSubsetWidth = x + (int) fSubsetWidth > info.width() ? info.width() - x : fSubsetWidth; const uint32_t currSubsetHeight = y + (int) fSubsetHeight > info.height() ? info.height() - y : fSubsetHeight; const uint32_t bpp = info.bytesPerPixel(); for (uint32_t y = 0; y < currSubsetHeight; y++) { scanlineDecoder->getScanlines(row.get(), 1, 0); memcpy(bitmap.getAddr(0, y), row.get() + x * bpp, currSubsetWidth * bpp); } } } } } else { // We create a color table here to satisfy allocPixels() when the output // type is kIndex8. It's okay that this is uninitialized since we never // use it. SkColorTable* colorTable = new SkColorTable(colors, 0); for (int count = 0; count < n; count++) { int width, height; SkAutoTDelete<SkImageDecoder> decoder(SkImageDecoder::Factory(fStream)); decoder->buildTileIndex(fStream->duplicate(), &width, &height); SkBitmap bitmap; // Note that we use the same bitmap for all of the subsets. // It might be larger than necessary for the end subsets. // If we do not include this step, decodeSubset() would allocate space // for the pixels automatically, but this would not allow us to reuse the // same bitmap as the other subsets. We want to reuse the same bitmap // because it gives a more fair comparison with SkCodec and is a common // use case of BitmapRegionDecoder. bitmap.allocPixels(SkImageInfo::Make(fSubsetWidth, fSubsetHeight, fColorType, kOpaque_SkAlphaType), nullptr, colorTable); for (int x = 0; x < width; x += fSubsetWidth) { for (int y = 0; y < height; y += fSubsetHeight) { const uint32_t currSubsetWidth = x + (int) fSubsetWidth > width ? width - x : fSubsetWidth; const uint32_t currSubsetHeight = y + (int) fSubsetHeight > height ? height - y : fSubsetHeight; SkIRect rect = SkIRect::MakeXYWH(x, y, currSubsetWidth, currSubsetHeight); decoder->decodeSubset(&bitmap, rect, fColorType); } } } } }
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; }