void SkBitmapDevice::drawBitmapRect(const SkDraw& draw, const SkBitmap& bitmap, const SkRect* src, const SkRect& dst, const SkPaint& paint, SkCanvas::SrcRectConstraint constraint) { SkMatrix matrix; SkRect bitmapBounds, tmpSrc, tmpDst; SkBitmap tmpBitmap; bitmapBounds.isetWH(bitmap.width(), bitmap.height()); // Compute matrix from the two rectangles if (src) { tmpSrc = *src; } else { tmpSrc = bitmapBounds; } matrix.setRectToRect(tmpSrc, dst, SkMatrix::kFill_ScaleToFit); LogDrawScaleFactor(SkMatrix::Concat(*draw.fMatrix, matrix), paint.getFilterQuality()); const SkRect* dstPtr = &dst; const SkBitmap* bitmapPtr = &bitmap; // clip the tmpSrc to the bounds of the bitmap, and recompute dstRect if // needed (if the src was clipped). No check needed if src==null. if (src) { if (!bitmapBounds.contains(*src)) { if (!tmpSrc.intersect(bitmapBounds)) { return; // nothing to draw } // recompute dst, based on the smaller tmpSrc matrix.mapRect(&tmpDst, tmpSrc); dstPtr = &tmpDst; } } if (src && !src->contains(bitmapBounds) && SkCanvas::kFast_SrcRectConstraint == constraint && paint.getFilterQuality() != kNone_SkFilterQuality) { // src is smaller than the bounds of the bitmap, and we are filtering, so we don't know // how much more of the bitmap we need, so we can't use extractSubset or drawBitmap, // but we must use a shader w/ dst bounds (which can access all of the bitmap needed). goto USE_SHADER; } if (src) { // since we may need to clamp to the borders of the src rect within // the bitmap, we extract a subset. const SkIRect srcIR = tmpSrc.roundOut(); if (!bitmap.extractSubset(&tmpBitmap, srcIR)) { return; } bitmapPtr = &tmpBitmap; // Since we did an extract, we need to adjust the matrix accordingly SkScalar dx = 0, dy = 0; if (srcIR.fLeft > 0) { dx = SkIntToScalar(srcIR.fLeft); } if (srcIR.fTop > 0) { dy = SkIntToScalar(srcIR.fTop); } if (dx || dy) { matrix.preTranslate(dx, dy); } SkRect extractedBitmapBounds; extractedBitmapBounds.isetWH(bitmapPtr->width(), bitmapPtr->height()); if (extractedBitmapBounds == tmpSrc) { // no fractional part in src, we can just call drawBitmap goto USE_DRAWBITMAP; } } else { USE_DRAWBITMAP: // We can go faster by just calling drawBitmap, which will concat the // matrix with the CTM, and try to call drawSprite if it can. If not, // it will make a shader and call drawRect, as we do below. if (CanApplyDstMatrixAsCTM(matrix, paint)) { draw.drawBitmap(*bitmapPtr, matrix, dstPtr, paint); return; } } USE_SHADER: // Since the shader need only live for our stack-frame, pass in a custom allocator. This // can save malloc calls, and signals to SkMakeBitmapShader to not try to copy the bitmap // if its mutable, since that precaution is not needed (give the short lifetime of the shader). SkTBlitterAllocator allocator; // construct a shader, so we can call drawRect with the dst auto s = SkMakeBitmapShader(*bitmapPtr, SkShader::kClamp_TileMode, SkShader::kClamp_TileMode, &matrix, kNever_SkCopyPixelsMode, &allocator); if (!s) { return; } // we deliberately add a ref, since the allocator wants to be the last owner s.get()->ref(); SkPaint paintWithShader(paint); paintWithShader.setStyle(SkPaint::kFill_Style); paintWithShader.setShader(s); // Call ourself, in case the subclass wanted to share this setup code // but handle the drawRect code themselves. this->drawRect(draw, *dstPtr, paintWithShader); }
sk_sp<SkShader> SkShader::MakeBitmapShader(const SkBitmap& src, TileMode tmx, TileMode tmy, const SkMatrix* localMatrix) { return SkMakeBitmapShader(src, tmx, tmy, localMatrix, nullptr); }
DEF_TEST(Serialization, reporter) { // Test matrix serialization { SkMatrix matrix = SkMatrix::I(); TestObjectSerialization(&matrix, reporter); } // Test path serialization { SkPath path; TestObjectSerialization(&path, reporter); } // Test region serialization { SkRegion region; TestObjectSerialization(®ion, reporter); } // Test xfermode serialization { TestXfermodeSerialization(reporter); } // Test color filter serialization { TestColorFilterSerialization(reporter); } // Test string serialization { SkString string("string"); TestObjectSerializationNoAlign<SkString, false>(&string, reporter); TestObjectSerializationNoAlign<SkString, true>(&string, reporter); } // Test rrect serialization { // SkRRect does not initialize anything. // An uninitialized SkRRect can be serialized, // but will branch on uninitialized data when deserialized. SkRRect rrect; SkRect rect = SkRect::MakeXYWH(1, 2, 20, 30); SkVector corners[4] = { {1, 2}, {2, 3}, {3,4}, {4,5} }; rrect.setRectRadii(rect, corners); TestAlignment(&rrect, reporter); } // Test readByteArray { unsigned char data[kArraySize] = { 1, 2, 3 }; TestArraySerialization(data, reporter); } // Test readColorArray { SkColor data[kArraySize] = { SK_ColorBLACK, SK_ColorWHITE, SK_ColorRED }; TestArraySerialization(data, reporter); } // Test readIntArray { int32_t data[kArraySize] = { 1, 2, 4, 8 }; TestArraySerialization(data, reporter); } // Test readPointArray { SkPoint data[kArraySize] = { {6, 7}, {42, 128} }; TestArraySerialization(data, reporter); } // Test readScalarArray { SkScalar data[kArraySize] = { SK_Scalar1, SK_ScalarHalf, SK_ScalarMax }; TestArraySerialization(data, reporter); } // Test invalid deserializations { SkImageInfo info = SkImageInfo::MakeN32Premul(kBitmapSize, kBitmapSize); SkBitmap validBitmap; validBitmap.setInfo(info); // Create a bitmap with a really large height SkBitmap invalidBitmap; invalidBitmap.setInfo(info.makeWH(info.width(), 1000000000)); // The deserialization should succeed, and the rendering shouldn't crash, // even when the device fails to initialize, due to its size TestBitmapSerialization(validBitmap, invalidBitmap, true, reporter); } // Test simple SkPicture serialization { SkPictureRecorder recorder; draw_something(recorder.beginRecording(SkIntToScalar(kBitmapSize), SkIntToScalar(kBitmapSize), nullptr, 0)); sk_sp<SkPicture> pict(recorder.finishRecordingAsPicture()); // Serialize picture SkBinaryWriteBuffer writer; pict->flatten(writer); size_t size = writer.bytesWritten(); SkAutoTMalloc<unsigned char> data(size); writer.writeToMemory(static_cast<void*>(data.get())); // Deserialize picture SkValidatingReadBuffer reader(static_cast<void*>(data.get()), size); sk_sp<SkPicture> readPict(SkPicture::MakeFromBuffer(reader)); REPORTER_ASSERT(reporter, readPict.get()); } TestPictureTypefaceSerialization(reporter); // Test SkLightingShader/NormalMapSource serialization { const int kTexSize = 2; SkLights::Builder builder; builder.add(SkLights::Light(SkColor3f::Make(1.0f, 1.0f, 1.0f), SkVector3::Make(1.0f, 0.0f, 0.0f))); builder.add(SkLights::Light(SkColor3f::Make(0.2f, 0.2f, 0.2f))); sk_sp<SkLights> fLights = builder.finish(); SkBitmap diffuse = sk_tool_utils::create_checkerboard_bitmap( kTexSize, kTexSize, sk_tool_utils::color_to_565(0x0), sk_tool_utils::color_to_565(0xFF804020), 8); SkRect bitmapBounds = SkRect::MakeIWH(diffuse.width(), diffuse.height()); SkMatrix matrix; SkRect r = SkRect::MakeWH(SkIntToScalar(kTexSize), SkIntToScalar(kTexSize)); matrix.setRectToRect(bitmapBounds, r, SkMatrix::kFill_ScaleToFit); SkMatrix ctm; ctm.setRotate(45); SkBitmap normals; normals.allocN32Pixels(kTexSize, kTexSize); sk_tool_utils::create_frustum_normal_map(&normals, SkIRect::MakeWH(kTexSize, kTexSize)); sk_sp<SkShader> normalMap = SkMakeBitmapShader(normals, SkShader::kClamp_TileMode, SkShader::kClamp_TileMode, &matrix, nullptr); sk_sp<SkNormalSource> normalSource = SkNormalSource::MakeFromNormalMap(std::move(normalMap), ctm); sk_sp<SkShader> lightingShader = SkLightingShader::Make(diffuse, fLights, &matrix, std::move(normalSource)); SkAutoTUnref<SkShader>(TestFlattenableSerialization(lightingShader.get(), true, reporter)); // TODO test equality? } }