void SkARGB32_Blitter::blitH(int x, int y, int width) { SkASSERT(x >= 0 && y >= 0 && x + width <= fDevice.width()); if (fSrcA == 0) { return; } uint32_t* device = fDevice.getAddr32(x, y); if (fSrcA == 255) { sk_memset32(device, fPMColor, width); } else { uint32_t color = fPMColor; unsigned dst_scale = SkAlpha255To256(255 - fSrcA); uint32_t prevDst = ~device[0]; // so we always fail the test the first time uint32_t result SK_INIT_TO_AVOID_WARNING; for (int i = 0; i < width; i++) { uint32_t currDst = device[i]; if (currDst != prevDst) { result = color + SkAlphaMulQ(currDst, dst_scale); prevDst = currDst; } device[i] = result; } } }
void SkARGB32_Black_Blitter::blitAntiH(int x, int y, const SkAlpha antialias[], const int16_t runs[]) { uint32_t* device = fDevice.getAddr32(x, y); SkPMColor black = (SkPMColor)(SK_A32_MASK << SK_A32_SHIFT); for (;;) { int count = runs[0]; SkASSERT(count >= 0); if (count <= 0) { return; } unsigned aa = antialias[0]; if (aa) { if (aa == 255) { sk_memset32(device, black, count); } else { SkPMColor src = aa << SK_A32_SHIFT; unsigned dst_scale = 256 - aa; int n = count; do { --n; device[n] = src + SkAlphaMulQ(device[n], dst_scale); } while (n > 0); } } runs += count; antialias += count; device += count; } }
void SkARGB32_Blitter::blitRect(int x, int y, int width, int height) { SkASSERT(x >= 0 && y >= 0 && x + width <= fDevice.width() && y + height <= fDevice.height()); if (fSrcA == 0) { return; } uint32_t* device = fDevice.getAddr32(x, y); uint32_t color = fPMColor; if (fSrcA == 255) { while (--height >= 0) { sk_memset32(device, color, width); device = (uint32_t*)((char*)device + fDevice.rowBytes()); } } else { unsigned dst_scale = SkAlpha255To256(255 - fSrcA); while (--height >= 0) { uint32_t prevDst = ~device[0]; uint32_t result SK_INIT_TO_AVOID_WARNING; for (int i = 0; i < width; i++) { uint32_t dst = device[i]; if (dst != prevDst) { result = color + SkAlphaMulQ(dst, dst_scale); prevDst = dst; } device[i] = result; } device = (uint32_t*)((char*)device + fDevice.rowBytes()); } } }
void SkRasterPipelineBlitter::blitH(int x, int y, int w) { fDstPtr = fDst.writable_addr(0,y); fCurrentY = y; if (fCanMemsetInBlitH) { switch (fDst.shiftPerPixel()) { case 0: memset ((uint8_t *)fDstPtr + x, fMemsetColor, w); return; case 1: sk_memset16((uint16_t*)fDstPtr + x, fMemsetColor, w); return; case 2: sk_memset32((uint32_t*)fDstPtr + x, fMemsetColor, w); return; case 3: sk_memset64((uint64_t*)fDstPtr + x, fMemsetColor, w); return; default: break; } } auto& p = fBlitH; if (p.empty()) { p.extend(fShader); if (fBlend != SkBlendMode::kSrc) { this->append_load_d(&p); this->append_blend(&p); this->maybe_clamp(&p); } this->append_store(&p); } p.run(x,w); }
void SkARGB32_Blitter::blitAntiH(int x, int y, const SkAlpha antialias[], const int16_t runs[]) { if (fSrcA == 0) { return; } uint32_t color = fPMColor; uint32_t* device = fDevice.getAddr32(x, y); unsigned opaqueMask = fSrcA; // if fSrcA is 0xFF, then we will catch the fast opaque case for (;;) { int count = runs[0]; SkASSERT(count >= 0); if (count <= 0) { return; } unsigned aa = antialias[0]; if (aa) { if ((opaqueMask & aa) == 255) { sk_memset32(device, color, count); } else { uint32_t sc = SkAlphaMulQ(color, aa); unsigned dst_scale = 255 - SkGetPackedA32(sc); int n = count; do { --n; device[n] = sc + SkAlphaMulQ(device[n], dst_scale); } while (n > 0); } } runs += count; antialias += count; device += count; } }
void SkARGB32_Blitter::blitAntiH(int x, int y, const SkAlpha antialias[], const int16_t runs[]) { if (fSrcA == 0) { return; } uint32_t color = fPMColor; uint32_t* device = fDevice.getAddr32(x, y); unsigned opaqueMask = fSrcA; // if fSrcA is 0xFF, then we will catch the fast opaque case for (;;) { int count = runs[0]; SkASSERT(count >= 0); if (count <= 0) { return; } unsigned aa = antialias[0]; if (aa) { if ((opaqueMask & aa) == 255) { sk_memset32(device, color, count); } else { uint32_t sc = SkAlphaMulQ(color, SkAlpha255To256(aa)); fColor32Proc(device, device, count, sc); } } runs += count; antialias += count; device += count; } }
void SkBaseDevice::drawAtlas(const SkImage* atlas, const SkRSXform xform[], const SkRect tex[], const SkColor colors[], int quadCount, SkBlendMode mode, const SkPaint& paint) { const int triCount = quadCount << 1; const int vertexCount = triCount * 3; uint32_t flags = SkVertices::kHasTexCoords_BuilderFlag; if (colors) { flags |= SkVertices::kHasColors_BuilderFlag; } SkVertices::Builder builder(SkVertices::kTriangles_VertexMode, vertexCount, 0, flags); SkPoint* vPos = builder.positions(); SkPoint* vTex = builder.texCoords(); SkColor* vCol = builder.colors(); for (int i = 0; i < quadCount; ++i) { SkPoint tmp[4]; xform[i].toQuad(tex[i].width(), tex[i].height(), tmp); vPos = quad_to_tris(vPos, tmp); tex[i].toQuad(tmp); vTex = quad_to_tris(vTex, tmp); if (colors) { sk_memset32(vCol, colors[i], 6); vCol += 6; } } SkPaint p(paint); p.setShader(atlas->makeShader()); this->drawVertices(builder.detach().get(), mode, p); }
static void test_read_pixels(skiatest::Reporter* reporter, SkImage* image) { const SkPMColor expected = SkPreMultiplyColor(SK_ColorWHITE); const SkPMColor notExpected = ~expected; const int w = 2, h = 2; const size_t rowBytes = w * sizeof(SkPMColor); SkPMColor pixels[w*h]; SkImageInfo info; info = SkImageInfo::MakeUnknown(w, h); REPORTER_ASSERT(reporter, !image->readPixels(info, pixels, rowBytes, 0, 0)); // out-of-bounds should fail info = SkImageInfo::MakeN32Premul(w, h); REPORTER_ASSERT(reporter, !image->readPixels(info, pixels, rowBytes, -w, 0)); REPORTER_ASSERT(reporter, !image->readPixels(info, pixels, rowBytes, 0, -h)); REPORTER_ASSERT(reporter, !image->readPixels(info, pixels, rowBytes, image->width(), 0)); REPORTER_ASSERT(reporter, !image->readPixels(info, pixels, rowBytes, 0, image->height())); // top-left should succeed sk_memset32(pixels, notExpected, w*h); REPORTER_ASSERT(reporter, image->readPixels(info, pixels, rowBytes, 0, 0)); REPORTER_ASSERT(reporter, has_pixels(pixels, w*h, expected)); // bottom-right should succeed sk_memset32(pixels, notExpected, w*h); REPORTER_ASSERT(reporter, image->readPixels(info, pixels, rowBytes, image->width() - w, image->height() - h)); REPORTER_ASSERT(reporter, has_pixels(pixels, w*h, expected)); // partial top-left should succeed sk_memset32(pixels, notExpected, w*h); REPORTER_ASSERT(reporter, image->readPixels(info, pixels, rowBytes, -1, -1)); REPORTER_ASSERT(reporter, pixels[3] == expected); REPORTER_ASSERT(reporter, has_pixels(pixels, w*h - 1, notExpected)); // partial bottom-right should succeed sk_memset32(pixels, notExpected, w*h); REPORTER_ASSERT(reporter, image->readPixels(info, pixels, rowBytes, image->width() - 1, image->height() - 1)); REPORTER_ASSERT(reporter, pixels[0] == expected); REPORTER_ASSERT(reporter, has_pixels(&pixels[1], w*h - 1, notExpected)); }
void drawBG(SkCanvas* canvas) { canvas->drawColor(0xFFDDDDDD); return; #if 0 SkColorTable ct; SkPMColor colors[] = { SK_ColorRED, SK_ColorBLUE }; ct.setColors(colors, 2); ct.setFlags(ct.getFlags() | SkColorTable::kColorsAreOpaque_Flag); SkBitmap bm; bm.setConfig(SkBitmap::kIndex8_Config, 20, 20, 21); bm.setColorTable(&ct); bm.allocPixels(); sk_memset16((uint16_t*)bm.getAddr8(0, 0), 0x0001, bm.rowBytes() * bm.height() / 2); #endif #if 0 SkBitmap bm; bm.setConfig(SkBitmap::kRGB_565_Config, 20, 20, 42); bm.allocPixels(); sk_memset32((uint32_t*)bm.getAddr16(0, 0), 0x0000FFFF, bm.rowBytes() * bm.height() / 4); #endif #if 1 SkBitmap bm; bm.setConfig(SkBitmap::kARGB_8888_Config, 20, 20); bm.allocPixels(); sk_memset32((uint32_t*)bm.getAddr32(0, 0), 0xFFDDDDDD, bm.rowBytes() * bm.height() / 4); #endif SkPaint paint; // SkShader* shader = SkShader::CreateBitmapShader(bm, false, SkPaint::kBilinear_FilterType, SkShader::kRepeat_TileMode); SkPoint pts[] = { 0, 0, SkIntToScalar(100), SkIntToScalar(0) }; SkColor colors[] = { SK_ColorBLACK, SK_ColorWHITE }; SkShader* shader = SkGradientShader::CreateLinear(pts, colors, NULL, 2, SkShader::kMirror_TileMode); paint.setShader(shader)->unref(); canvas->drawPaint(paint); }
template <DstType D> void src_1(const SkXfermode*, uint32_t dst[], const SkPM4f* src, int count, const SkAlpha aa[]) { const Sk4f s4 = src->to4f_pmorder(); if (aa) { if (D == kLinear_Dst) { // operate in bias-255 space for src and dst const Sk4f& s4_255 = s4 * Sk4f(255); while (count >= 4) { Sk4f aa4 = SkNx_cast<float>(Sk4b::Load(aa)) * Sk4f(1/255.f); Sk4f r0 = lerp(s4_255, to_4f(dst[0]), Sk4f(aa4[0])) + Sk4f(0.5f); Sk4f r1 = lerp(s4_255, to_4f(dst[1]), Sk4f(aa4[1])) + Sk4f(0.5f); Sk4f r2 = lerp(s4_255, to_4f(dst[2]), Sk4f(aa4[2])) + Sk4f(0.5f); Sk4f r3 = lerp(s4_255, to_4f(dst[3]), Sk4f(aa4[3])) + Sk4f(0.5f); Sk4f_ToBytes((uint8_t*)dst, r0, r1, r2, r3); dst += 4; aa += 4; count -= 4; } } else { // kSRGB while (count >= 4) { Sk4f aa4 = SkNx_cast<float>(Sk4b::Load(aa)) * Sk4f(1/255.0f); /* If we ever natively support convert 255_linear -> 255_srgb, then perhaps * it would be faster (and possibly allow more code sharing with kLinear) to * stay in that space. */ Sk4f r0 = lerp(s4, load_dst<D>(dst[0]), Sk4f(aa4[0])); Sk4f r1 = lerp(s4, load_dst<D>(dst[1]), Sk4f(aa4[1])); Sk4f r2 = lerp(s4, load_dst<D>(dst[2]), Sk4f(aa4[2])); Sk4f r3 = lerp(s4, load_dst<D>(dst[3]), Sk4f(aa4[3])); Sk4f_ToBytes((uint8_t*)dst, linear_unit_to_srgb_255f(r0), linear_unit_to_srgb_255f(r1), linear_unit_to_srgb_255f(r2), linear_unit_to_srgb_255f(r3)); dst += 4; aa += 4; count -= 4; } } for (int i = 0; i < count; ++i) { unsigned a = aa[i]; Sk4f d4 = load_dst<D>(dst[i]); dst[i] = store_dst<D>(lerp(s4, d4, a)); } } else { sk_memset32(dst, store_dst<D>(s4), count); } }
void SkSampler::Fill(const SkImageInfo& info, void* dst, size_t rowBytes, SkCodec::ZeroInitialized zeroInit) { SkASSERT(dst != nullptr); if (SkCodec::kYes_ZeroInitialized == zeroInit) { return; } const int width = info.width(); const int numRows = info.height(); // Use the proper memset routine to fill the remaining bytes switch (info.colorType()) { case kRGBA_8888_SkColorType: case kBGRA_8888_SkColorType: { uint32_t* dstRow = (uint32_t*) dst; for (int row = 0; row < numRows; row++) { sk_memset32(dstRow, 0, width); dstRow = SkTAddOffset<uint32_t>(dstRow, rowBytes); } break; } case kRGB_565_SkColorType: { uint16_t* dstRow = (uint16_t*) dst; for (int row = 0; row < numRows; row++) { sk_memset16(dstRow, 0, width); dstRow = SkTAddOffset<uint16_t>(dstRow, rowBytes); } break; } case kGray_8_SkColorType: { uint8_t* dstRow = (uint8_t*) dst; for (int row = 0; row < numRows; row++) { memset(dstRow, 0, width); dstRow = SkTAddOffset<uint8_t>(dstRow, rowBytes); } break; } case kRGBA_F16_SkColorType: { uint64_t* dstRow = (uint64_t*) dst; for (int row = 0; row < numRows; row++) { sk_memset64(dstRow, 0, width); dstRow = SkTAddOffset<uint64_t>(dstRow, rowBytes); } break; } default: SkCodecPrintf("Error: Unsupported dst color type for fill(). Doing nothing.\n"); SkASSERT(false); break; } }
static void clear_srgb(const SkXfermode*, uint32_t dst[], const SkPM4f[], int count, const SkAlpha aa[]) { if (aa) { for (int i = 0; i < count; ++i) { if (aa[i]) { Sk4f d = Sk4f_fromS32(dst[i]) * Sk4f((255 - aa[i]) * (1/255.0f)); dst[i] = Sk4f_toS32(d); } } } else { sk_memset32(dst, 0, count); } }
static void test_32(skiatest::Reporter* reporter) { uint32_t buffer[TOTAL]; for (int count = 0; count < MAX_COUNT; ++count) { for (int alignment = 0; alignment < MAX_ALIGNMENT; ++alignment) { set_zero(buffer, sizeof(buffer)); uint32_t* base = &buffer[PAD + alignment]; sk_memset32(base, VALUE32, count); compare32(buffer, 0, PAD + alignment); compare32(base, VALUE32, count); compare32(base + count, 0, TOTAL - count - PAD - alignment); } } }
static void clear_linear(const SkXfermode*, uint32_t dst[], const SkPM4f[], int count, const SkAlpha aa[]) { if (aa) { for (int i = 0; i < count; ++i) { unsigned a = aa[i]; if (a) { SkPMColor dstC = dst[i]; SkPMColor C = 0; if (0xFF != a) { C = SkFourByteInterp(C, dstC, a); } dst[i] = C; } } } else { sk_memset32(dst, 0, count); } }
bool onGetPixels(const SkImageInfo& info, void* pixels, size_t rowBytes, SkPMColor ctable[], int* ctableCount) override { REPORTER_ASSERT(fReporter, pixels != NULL); REPORTER_ASSERT(fReporter, rowBytes >= info.minRowBytes()); if (fType != kSucceedGetPixels_TestType) { return false; } if (info.colorType() != kN32_SkColorType) { return false; } char* bytePtr = static_cast<char*>(pixels); for (int y = 0; y < info.height(); ++y) { sk_memset32(reinterpret_cast<SkColor*>(bytePtr), TestImageGenerator::Color(), info.width()); bytePtr += rowBytes; } return true; }
void xfer_pm4_proc_1(const SkXfermode::PM4fState& state, uint32_t dst[], const SkPM4f& src, int count, const SkAlpha aa[]) { uint32_t pm; pm4f_to_linear_32(&pm, &src, 1); const int N = 128; SkPMColor tmp[N]; sk_memset32(tmp, pm, SkMin32(count, N)); while (count > 0) { const int n = SkMin32(count, N); state.fXfer->xfer32(dst, tmp, n, aa); dst += n; if (aa) { aa += n; } count -= n; } }
static void fillRect(SkBitmap* bm, GifWord left, GifWord top, GifWord width, GifWord height, uint32_t col) { int bmWidth = bm->width(); int bmHeight = bm->height(); uint32_t* dst = bm->getAddr32(left, top); GifWord copyWidth = width; if (left + copyWidth > bmWidth) { copyWidth = bmWidth - left; } GifWord copyHeight = height; if (top + copyHeight > bmHeight) { copyHeight = bmHeight - top; } for (; copyHeight > 0; copyHeight--) { sk_memset32(dst, col, copyWidth); dst += bmWidth; } }
template <DstType D> void src_1(const SkXfermode::PM4fState& state, uint32_t dst[], const SkPM4f& src, int count, const SkAlpha aa[]) { const Sk4f r4 = Sk4f::Load(src.fVec); // src always overrides dst const uint32_t r32 = store_dst<D>(r4); if (aa) { for (int i = 0; i < count; ++i) { unsigned a = aa[i]; if (0 == a) { continue; } if (a != 0xFF) { Sk4f d4 = load_dst<D>(dst[i]); dst[i] = store_dst<D>(lerp(r4, d4, a)); } else { dst[i] = r32; } } } else { sk_memset32(dst, r32, count); } }
DEF_GPUTEST_FOR_RENDERING_CONTEXTS(ApplyGamma, reporter, ctxInfo) { GrContext* context = ctxInfo.grContext(); static const int kW = 256; static const int kH = 256; static const size_t kRowBytes = sizeof(uint32_t) * kW; GrSurfaceDesc baseDesc; baseDesc.fConfig = kRGBA_8888_GrPixelConfig; baseDesc.fWidth = kW; baseDesc.fHeight = kH; const SkImageInfo ii = SkImageInfo::MakeN32Premul(kW, kH); SkAutoTMalloc<uint32_t> srcPixels(kW * kH); for (int y = 0; y < kH; ++y) { for (int x = 0; x < kW; ++x) { srcPixels.get()[y*kW+x] = SkPreMultiplyARGB(x, y, x, 0xFF); } } SkBitmap bm; bm.installPixels(ii, srcPixels.get(), kRowBytes); SkAutoTMalloc<uint32_t> read(kW * kH); // We allow more error on GPUs with lower precision shader variables. float error = context->caps()->shaderCaps()->floatPrecisionVaries() ? 1.2f : 0.5f; for (auto toSRGB : { false, true }) { sk_sp<SkSurface> dst(SkSurface::MakeRenderTarget(context, SkBudgeted::kNo, ii)); if (!dst) { ERRORF(reporter, "Could not create surfaces for copy surface test."); continue; } SkCanvas* dstCanvas = dst->getCanvas(); dstCanvas->clear(SK_ColorRED); dstCanvas->flush(); SkPaint gammaPaint; gammaPaint.setBlendMode(SkBlendMode::kSrc); gammaPaint.setColorFilter(toSRGB ? SkColorFilter::MakeLinearToSRGBGamma() : SkColorFilter::MakeSRGBToLinearGamma()); dstCanvas->drawBitmap(bm, 0, 0, &gammaPaint); dstCanvas->flush(); sk_memset32(read.get(), 0, kW * kH); if (!dstCanvas->readPixels(ii, read.get(), kRowBytes, 0, 0)) { ERRORF(reporter, "Error calling readPixels"); continue; } bool abort = false; // Validate that pixels were copied/transformed correctly. for (int y = 0; y < kH && !abort; ++y) { for (int x = 0; x < kW && !abort; ++x) { uint32_t r = read.get()[y * kW + x]; uint32_t s = srcPixels.get()[y * kW + x]; uint32_t expected; if (!check_gamma(s, r, toSRGB, error, &expected)) { ERRORF(reporter, "Expected dst %d,%d to contain 0x%08x " "from src 0x%08x and mode %s. Got %08x", x, y, expected, s, toSRGB ? "ToSRGB" : "ToLinear", r); abort = true; break; } } } } }
void filterSpan(const SkPMColor shader[], int count, SkPMColor result[]) const override { sk_memset32(result, this->getPMColor(), count); }
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 }, }; static const SkImageInfo kImageInfos[] { SkImageInfo::Make(kW, kH, kRGBA_8888_SkColorType, kPremul_SkAlphaType), SkImageInfo::Make(kW, kH, kBGRA_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 sRenderable : {GrRenderable::kYes, GrRenderable::kNo}) { for (auto dRenderable : {GrRenderable::kYes, GrRenderable::kNo}) { for (auto srcRect : kSrcRects) { for (auto dstPoint : kDstPoints) { for (auto ii: kImageInfos) { auto src = sk_gpu_test::MakeTextureProxyFromData( context, sRenderable, kW, kH, ii.colorType(), sOrigin, srcPixels.get(), kRowBytes); auto dst = sk_gpu_test::MakeTextureProxyFromData( context, dRenderable, kW, kH, ii.colorType(), dOrigin, dstPixels.get(), kRowBytes); // Should always work if the color type is RGBA, but may not work // for BGRA if (ii.colorType() == kRGBA_8888_SkColorType) { if (!src || !dst) { ERRORF(reporter, "Could not create surfaces for copy surface test."); continue; } } else { GrPixelConfig config = SkColorType2GrPixelConfig(kBGRA_8888_SkColorType); if (!context->priv().caps()->isConfigTexturable(config)) { continue; } if (!src || !dst) { ERRORF(reporter, "Could not create surfaces for copy surface test."); continue; } } sk_sp<GrSurfaceContext> dstContext = context->priv().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; } } } } } } } } } } } }
Error HWUISink::draw(const Src& src, SkBitmap* dst, SkWStream*, SkString*) const { // Do all setup in this function because we don't know the size // for the RenderNode and RenderProxy during the constructor. // In practice this doesn't seem too expensive. const SkISize size = src.size(); // Based on android::SurfaceTexture_init() android::sp<android::IGraphicBufferProducer> producer; android::sp<android::IGraphicBufferConsumer> consumer; android::BufferQueue::createBufferQueue(&producer, &consumer); // Consumer setup android::sp<android::CpuConsumer> cpuConsumer = new android::CpuConsumer(consumer, 1); cpuConsumer->setName(android::String8("SkiaTestClient")); cpuConsumer->setDefaultBufferSize(size.width(), size.height()); // Producer setup android::sp<android::Surface> surface = new android::Surface(producer); native_window_set_buffers_dimensions(surface.get(), size.width(), size.height()); native_window_set_buffers_format(surface.get(), android::PIXEL_FORMAT_RGBA_8888); native_window_set_usage(surface.get(), GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_NEVER | GRALLOC_USAGE_HW_RENDER); // RenderNode setup based on hwui/tests/main.cpp:TreeContentAnimation SkAutoTDelete<android::uirenderer::RenderNode> rootNode (new android::uirenderer::RenderNode()); rootNode->incStrong(nullptr); // Values set here won't be applied until the framework has called // RenderNode::pushStagingPropertiesChanges() during RenderProxy::syncAndDrawFrame(). rootNode->mutateStagingProperties().setLeftTopRightBottom(0, 0, size.width(), size.height()); rootNode->setPropertyFieldsDirty(android::uirenderer::RenderNode::X | android::uirenderer::RenderNode::Y); rootNode->mutateStagingProperties().setClipToBounds(false); rootNode->setPropertyFieldsDirty(android::uirenderer::RenderNode::GENERIC); // RenderProxy setup based on hwui/tests/main.cpp:TreeContentAnimation ContextFactory factory; SkAutoTDelete<android::uirenderer::renderthread::RenderProxy> proxy (new android::uirenderer::renderthread::RenderProxy(false, rootNode, &factory)); proxy->loadSystemProperties(); proxy->initialize(surface.get()); float lightX = size.width() / 2.0f; android::uirenderer::Vector3 lightVector { lightX, dp(-200.0f), dp(800.0f) }; proxy->setup(size.width(), size.height(), lightVector, dp(800.0f), 255 * 0.075f, 255 * 0.15f, kDensity); // Do the draw SkAutoTDelete<android::uirenderer::DisplayListRenderer> renderer (new android::uirenderer::DisplayListRenderer()); renderer->setViewport(size.width(), size.height()); renderer->prepare(); renderer->clipRect(0, 0, size.width(), size.height(), SkRegion::Op::kReplace_Op); Error err = src.draw(renderer->asSkCanvas()); if (!err.isEmpty()) { return err; } renderer->finish(); rootNode->setStagingDisplayList(renderer->finishRecording()); proxy->syncAndDrawFrame(); proxy->fence(); // Capture pixels SkImageInfo destinationConfig = SkImageInfo::Make(size.width(), size.height(), kRGBA_8888_SkColorType, kPremul_SkAlphaType); dst->allocPixels(destinationConfig); sk_memset32((uint32_t*) dst->getPixels(), SK_ColorRED, size.width() * size.height()); android::CpuConsumer::LockedBuffer nativeBuffer; android::status_t retval = cpuConsumer->lockNextBuffer(&nativeBuffer); if (retval == android::BAD_VALUE) { SkDebugf("HWUISink::draw() got no buffer; returning transparent"); // No buffer ready to read - commonly triggered by dm sending us // a no-op source, or calling code that doesn't do anything on this // backend. dst->eraseColor(SK_ColorTRANSPARENT); return ""; } else if (retval) { return SkStringPrintf("Failed to lock buffer to read pixels: %d.", retval); } // Move the pixels into the destination SkBitmap SK_ALWAYSBREAK(nativeBuffer.format == android::PIXEL_FORMAT_RGBA_8888 && "Native buffer not RGBA!"); SkImageInfo nativeConfig = SkImageInfo::Make(nativeBuffer.width, nativeBuffer.height, kRGBA_8888_SkColorType, kPremul_SkAlphaType); // Android stride is in pixels, Skia stride is in bytes SkBitmap nativeWrapper; bool success = nativeWrapper.installPixels(nativeConfig, nativeBuffer.data, nativeBuffer.stride * 4); if (!success) { return "Failed to wrap HWUI buffer in a SkBitmap"; } SK_ALWAYSBREAK(dst->colorType() == kRGBA_8888_SkColorType && "Destination buffer not RGBA!"); success = nativeWrapper.readPixels(destinationConfig, dst->getPixels(), dst->rowBytes(), 0, 0); if (!success) { return "Failed to extract pixels from HWUI buffer"; } cpuConsumer->unlockBuffer(nativeBuffer); return ""; }
virtual void filterSpan(const SkPMColor shader[], int count, SkPMColor result[]) { sk_memset32(result, fPMColor, count); }
void SkSampler::Fill(const SkImageInfo& info, void* dst, size_t rowBytes, uint64_t colorOrIndex, SkCodec::ZeroInitialized zeroInit) { SkASSERT(dst != nullptr); // Calculate bytes to fill. const size_t bytesToFill = info.computeByteSize(rowBytes); const int width = info.width(); const int numRows = info.height(); // Use the proper memset routine to fill the remaining bytes switch (info.colorType()) { case kRGBA_8888_SkColorType: case kBGRA_8888_SkColorType: { // If memory is zero initialized, we may not need to fill uint32_t color = (uint32_t) colorOrIndex; if (SkCodec::kYes_ZeroInitialized == zeroInit && 0 == color) { return; } uint32_t* dstRow = (uint32_t*) dst; for (int row = 0; row < numRows; row++) { sk_memset32((uint32_t*) dstRow, color, width); dstRow = SkTAddOffset<uint32_t>(dstRow, rowBytes); } break; } case kRGB_565_SkColorType: { // If the destination is k565, the caller passes in a 16-bit color. // We will not assert that the high bits of colorOrIndex must be zeroed. // This allows us to take advantage of the fact that the low 16 bits of an // SKPMColor may be a valid a 565 color. For example, the low 16 // bits of SK_ColorBLACK are identical to the 565 representation // for black. // If memory is zero initialized, we may not need to fill uint16_t color = (uint16_t) colorOrIndex; if (SkCodec::kYes_ZeroInitialized == zeroInit && 0 == color) { return; } uint16_t* dstRow = (uint16_t*) dst; for (int row = 0; row < numRows; row++) { sk_memset16((uint16_t*) dstRow, color, width); dstRow = SkTAddOffset<uint16_t>(dstRow, rowBytes); } break; } case kGray_8_SkColorType: // If the destination is kGray, the caller passes in an 8-bit color. // We will not assert that the high bits of colorOrIndex must be zeroed. // This allows us to take advantage of the fact that the low 8 bits of an // SKPMColor may be a valid a grayscale color. For example, the low 8 // bits of SK_ColorBLACK are identical to the grayscale representation // for black. // If memory is zero initialized, we may not need to fill if (SkCodec::kYes_ZeroInitialized == zeroInit && 0 == (uint8_t) colorOrIndex) { return; } memset(dst, (uint8_t) colorOrIndex, bytesToFill); break; case kRGBA_F16_SkColorType: { uint64_t color = colorOrIndex; if (SkCodec::kYes_ZeroInitialized == zeroInit && 0 == color) { return; } uint64_t* dstRow = (uint64_t*) dst; for (int row = 0; row < numRows; row++) { sk_memset64((uint64_t*) dstRow, color, width); dstRow = SkTAddOffset<uint64_t>(dstRow, rowBytes); } break; } default: SkCodecPrintf("Error: Unsupported dst color type for fill(). Doing nothing.\n"); SkASSERT(false); break; } }
void SkColorShader::ColorShaderContext::shadeSpan(int x, int y, SkPMColor span[], int count) { sk_memset32(span, fPMColor, count); }
bool SkPixmap::erase(SkColor color, const SkIRect& inArea) const { if (nullptr == fPixels) { return false; } SkIRect area; if (!area.intersect(this->bounds(), inArea)) { return false; } U8CPU a = SkColorGetA(color); U8CPU r = SkColorGetR(color); U8CPU g = SkColorGetG(color); U8CPU b = SkColorGetB(color); int height = area.height(); const int width = area.width(); const int rowBytes = this->rowBytes(); switch (this->colorType()) { case kGray_8_SkColorType: { if (255 != a) { r = SkMulDiv255Round(r, a); g = SkMulDiv255Round(g, a); b = SkMulDiv255Round(b, a); } int gray = SkComputeLuminance(r, g, b); uint8_t* p = this->writable_addr8(area.fLeft, area.fTop); while (--height >= 0) { memset(p, gray, width); p += rowBytes; } break; } case kAlpha_8_SkColorType: { uint8_t* p = this->writable_addr8(area.fLeft, area.fTop); while (--height >= 0) { memset(p, a, width); p += rowBytes; } break; } case kARGB_4444_SkColorType: case kRGB_565_SkColorType: { uint16_t* p = this->writable_addr16(area.fLeft, area.fTop); uint16_t v; // make rgb premultiplied if (255 != a) { r = SkMulDiv255Round(r, a); g = SkMulDiv255Round(g, a); b = SkMulDiv255Round(b, a); } if (kARGB_4444_SkColorType == this->colorType()) { v = pack_8888_to_4444(a, r, g, b); } else { v = SkPackRGB16(r >> (8 - SK_R16_BITS), g >> (8 - SK_G16_BITS), b >> (8 - SK_B16_BITS)); } while (--height >= 0) { sk_memset16(p, v, width); p = (uint16_t*)((char*)p + rowBytes); } break; } case kBGRA_8888_SkColorType: case kRGBA_8888_SkColorType: { uint32_t* p = this->writable_addr32(area.fLeft, area.fTop); if (255 != a && kPremul_SkAlphaType == this->alphaType()) { r = SkMulDiv255Round(r, a); g = SkMulDiv255Round(g, a); b = SkMulDiv255Round(b, a); } uint32_t v = kRGBA_8888_SkColorType == this->colorType() ? SkPackARGB_as_RGBA(a, r, g, b) : SkPackARGB_as_BGRA(a, r, g, b); while (--height >= 0) { sk_memset32(p, v, width); p = (uint32_t*)((char*)p + rowBytes); } break; } default: return false; // no change, so don't call notifyPixelsChanged() } return true; }
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; GrSurfaceDesc baseDesc; baseDesc.fConfig = kRGBA_8888_GrPixelConfig; baseDesc.fWidth = kW; baseDesc.fHeight = kH; 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 }, }; SkAutoTMalloc<uint32_t> read(kW * kH); for (auto sOrigin : {kBottomLeft_GrSurfaceOrigin, kTopLeft_GrSurfaceOrigin}) { for (auto dOrigin : {kBottomLeft_GrSurfaceOrigin, kTopLeft_GrSurfaceOrigin}) { for (auto sFlags: {kRenderTarget_GrSurfaceFlag, kNone_GrSurfaceFlags}) { for (auto dFlags: {kRenderTarget_GrSurfaceFlag, kNone_GrSurfaceFlags}) { for (auto srcRect : kSrcRects) { for (auto dstPoint : kDstPoints) { GrSurfaceDesc srcDesc = baseDesc; srcDesc.fOrigin = sOrigin; srcDesc.fFlags = sFlags; GrSurfaceDesc dstDesc = baseDesc; dstDesc.fOrigin = dOrigin; dstDesc.fFlags = dFlags; SkAutoTUnref<GrTexture> src( context->textureProvider()->createTexture(srcDesc, SkBudgeted::kNo, srcPixels.get(), kRowBytes)); SkAutoTUnref<GrTexture> dst( context->textureProvider()->createTexture(dstDesc, SkBudgeted::kNo, dstPixels.get(), kRowBytes)); if (!src || !dst) { ERRORF(reporter, "Could not create surfaces for copy surface test."); continue; } bool result = context->copySurface(dst, src, 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 (!dst->readPixels(0, 0, kW, kH, baseDesc.fConfig, read.get(), kRowBytes)) { 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; } } } } } } } } } } }
// Note: SkColorTable claims to store SkPMColors, which is not necessarily // the case here. bool SkPngCodec::createColorTable(SkColorType dstColorType, bool premultiply, int* ctableCount) { int numColors; png_color* palette; if (!png_get_PLTE(fPng_ptr, fInfo_ptr, &palette, &numColors)) { return false; } // Note: These are not necessarily SkPMColors. SkPMColor colorPtr[256]; png_bytep alphas; int numColorsWithAlpha = 0; if (png_get_tRNS(fPng_ptr, fInfo_ptr, &alphas, &numColorsWithAlpha, nullptr)) { // Choose which function to use to create the color table. If the final destination's // colortype is unpremultiplied, the color table will store unpremultiplied colors. PackColorProc proc = choose_pack_color_proc(premultiply, dstColorType); for (int i = 0; i < numColorsWithAlpha; i++) { // We don't have a function in SkOpts that combines a set of alphas with a set // of RGBs. We could write one, but it's hardly worth it, given that this // is such a small fraction of the total decode time. colorPtr[i] = proc(alphas[i], palette->red, palette->green, palette->blue); palette++; } } if (numColorsWithAlpha < numColors) { // The optimized code depends on a 3-byte png_color struct with the colors // in RGB order. These checks make sure it is safe to use. static_assert(3 == sizeof(png_color), "png_color struct has changed. Opts are broken."); #ifdef SK_DEBUG SkASSERT(&palette->red < &palette->green); SkASSERT(&palette->green < &palette->blue); #endif if (is_rgba(dstColorType)) { SkOpts::RGB_to_RGB1(colorPtr + numColorsWithAlpha, palette, numColors - numColorsWithAlpha); } else { SkOpts::RGB_to_BGR1(colorPtr + numColorsWithAlpha, palette, numColors - numColorsWithAlpha); } } // Pad the color table with the last color in the table (or black) in the case that // invalid pixel indices exceed the number of colors in the table. const int maxColors = 1 << fBitDepth; if (numColors < maxColors) { SkPMColor lastColor = numColors > 0 ? colorPtr[numColors - 1] : SK_ColorBLACK; sk_memset32(colorPtr + numColors, lastColor, maxColors - numColors); } // Set the new color count. if (ctableCount != nullptr) { *ctableCount = maxColors; } fColorTable.reset(new SkColorTable(colorPtr, maxColors)); return true; }
void SkGifCodec::initializeColorTable(const SkImageInfo& dstInfo, SkPMColor* inputColorPtr, int* inputColorCount) { // Set up our own color table const uint32_t maxColors = 256; SkPMColor colorPtr[256]; if (NULL != inputColorCount) { // We set the number of colors to maxColors in order to ensure // safe memory accesses. Otherwise, an invalid pixel could // access memory outside of our color table array. *inputColorCount = maxColors; } // Get local color table ColorMapObject* colorMap = fGif->Image.ColorMap; // If there is no local color table, use the global color table if (NULL == colorMap) { colorMap = fGif->SColorMap; } uint32_t colorCount = 0; if (NULL != colorMap) { colorCount = colorMap->ColorCount; // giflib guarantees these properties SkASSERT(colorCount == (unsigned) (1 << (colorMap->BitsPerPixel))); SkASSERT(colorCount <= 256); PackColorProc proc = choose_pack_color_proc(false, dstInfo.colorType()); for (uint32_t i = 0; i < colorCount; i++) { colorPtr[i] = proc(0xFF, colorMap->Colors[i].Red, colorMap->Colors[i].Green, colorMap->Colors[i].Blue); } } // Fill in the color table for indices greater than color count. // This allows for predictable, safe behavior. if (colorCount > 0) { // Gifs have the option to specify the color at a single index of the color // table as transparent. If the transparent index is greater than the // colorCount, we know that there is no valid transparent color in the color // table. If there is not valid transparent index, we will try to use the // backgroundIndex as the fill index. If the backgroundIndex is also not // valid, we will let fFillIndex default to 0 (it is set to zero in the // constructor). This behavior is not specified but matches // SkImageDecoder_libgif. uint32_t backgroundIndex = fGif->SBackGroundColor; if (fTransIndex < colorCount) { colorPtr[fTransIndex] = SK_ColorTRANSPARENT; fFillIndex = fTransIndex; } else if (backgroundIndex < colorCount) { fFillIndex = backgroundIndex; } for (uint32_t i = colorCount; i < maxColors; i++) { colorPtr[i] = colorPtr[fFillIndex]; } } else { sk_memset32(colorPtr, 0xFF000000, maxColors); } fColorTable.reset(new SkColorTable(colorPtr, maxColors)); copy_color_table(dstInfo, this->fColorTable, inputColorPtr, inputColorCount); }
/** * Fill with all zeros, which will never match any value from fill_4x4_pixels */ static void clear_4x4_pixels(SkPMColor colors[16]) { sk_memset32(colors, 0, 16); }