static void test_matrix_recttorect(skiatest::Reporter* reporter) { SkRect src, dst; SkMatrix matrix; src.set(0, 0, SK_Scalar1*10, SK_Scalar1*10); dst = src; matrix.setRectToRect(src, dst, SkMatrix::kFill_ScaleToFit); REPORTER_ASSERT(reporter, SkMatrix::kIdentity_Mask == matrix.getType()); REPORTER_ASSERT(reporter, matrix.rectStaysRect()); dst.offset(SK_Scalar1, SK_Scalar1); matrix.setRectToRect(src, dst, SkMatrix::kFill_ScaleToFit); REPORTER_ASSERT(reporter, SkMatrix::kTranslate_Mask == matrix.getType()); REPORTER_ASSERT(reporter, matrix.rectStaysRect()); dst.fRight += SK_Scalar1; matrix.setRectToRect(src, dst, SkMatrix::kFill_ScaleToFit); REPORTER_ASSERT(reporter, (SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask) == matrix.getType()); REPORTER_ASSERT(reporter, matrix.rectStaysRect()); dst = src; dst.fRight = src.fRight * 2; matrix.setRectToRect(src, dst, SkMatrix::kFill_ScaleToFit); REPORTER_ASSERT(reporter, SkMatrix::kScale_Mask == matrix.getType()); REPORTER_ASSERT(reporter, matrix.rectStaysRect()); }
// This does a lot of computation to resample only the portion of the bitmap // that will only be drawn. This is critical for performance since when we are // scrolling, for example, we are only drawing a small strip of the image. // Resampling the whole image every time is very slow, so this speeds up things // dramatically. // // Note: this code is only used when the canvas transformation is limited to // scaling or translation. static void drawResampledBitmap(SkCanvas& canvas, SkPaint& paint, const NativeImageSkia& bitmap, const SkRect& srcRect, const SkRect& destRect) { #if PLATFORM(CHROMIUM) TRACE_EVENT0("skia", "drawResampledBitmap"); #endif // We want to scale |destRect| with transformation in the canvas to obtain // the final scale. The final scale is a combination of scale transform // in canvas and explicit scaling (srcRect and destRect). SkRect screenRect; canvas.getTotalMatrix().mapRect(&screenRect, destRect); float realScaleX = screenRect.width() / srcRect.width(); float realScaleY = screenRect.height() / srcRect.height(); // This part of code limits scaling only to visible portion in the SkRect destRectVisibleSubset; ClipRectToCanvas(canvas, destRect, &destRectVisibleSubset); // ClipRectToCanvas often overshoots, resulting in a larger region than our // original destRect. Intersecting gets us back inside. if (!destRectVisibleSubset.intersect(destRect)) return; // Nothing visible in destRect. // Find the corresponding rect in the source image. SkMatrix destToSrcTransform; SkRect srcRectVisibleSubset; destToSrcTransform.setRectToRect(destRect, srcRect, SkMatrix::kFill_ScaleToFit); destToSrcTransform.mapRect(&srcRectVisibleSubset, destRectVisibleSubset); SkRect scaledSrcRect; SkIRect enclosingScaledSrcRect; SkBitmap scaledImageFragment = extractScaledImageFragment(bitmap, srcRectVisibleSubset, realScaleX, realScaleY, &scaledSrcRect, &enclosingScaledSrcRect); // Expand the destination rectangle because the source rectangle was // expanded to fit to integer boundaries. SkMatrix scaledSrcToDestTransform; scaledSrcToDestTransform.setRectToRect(scaledSrcRect, destRectVisibleSubset, SkMatrix::kFill_ScaleToFit); SkRect enclosingDestRect; enclosingDestRect.set(enclosingScaledSrcRect); scaledSrcToDestTransform.mapRect(&enclosingDestRect); // The reason we do clipping is because Skia doesn't support SkRect as // source rect. See http://crbug.com/145540. // When Skia supports then use this as the source rect to replace 0. // // scaledSrcRect.offset(-enclosingScaledSrcRect.x(), -enclosingScaledSrcRect.y()); canvas.save(); canvas.clipRect(destRectVisibleSubset); // Because the image fragment is generated with an approxmiated scaling // factor. This draw will perform a close to 1 scaling. // // NOTE: For future optimization. If the difference in scale is so small // that Skia doesn't produce a difference then we can just blit it directly // to enhance performance. canvas.drawBitmapRect(scaledImageFragment, 0, enclosingDestRect, &paint); canvas.restore(); }
void SkGpuDevice::drawTextureProducer(GrTextureProducer* producer, const SkRect* srcRect, const SkRect* dstRect, SkCanvas::SrcRectConstraint constraint, const SkMatrix& viewMatrix, const GrClip& clip, const SkPaint& paint) { // This is the funnel for all non-tiled bitmap/image draw calls. Log a histogram entry. SK_HISTOGRAM_BOOLEAN("DrawTiled", false); // Figure out the actual dst and src rect by clipping the src rect to the bounds of the // adjuster. If the src rect is clipped then the dst rect must be recomputed. Also determine // the matrix that maps the src rect to the dst rect. SkRect clippedSrcRect; SkRect clippedDstRect; const SkRect srcBounds = SkRect::MakeIWH(producer->width(), producer->height()); SkMatrix srcToDstMatrix; if (srcRect) { if (!dstRect) { dstRect = &srcBounds; } if (!srcBounds.contains(*srcRect)) { clippedSrcRect = *srcRect; if (!clippedSrcRect.intersect(srcBounds)) { return; } if (!srcToDstMatrix.setRectToRect(*srcRect, *dstRect, SkMatrix::kFill_ScaleToFit)) { return; } srcToDstMatrix.mapRect(&clippedDstRect, clippedSrcRect); } else { clippedSrcRect = *srcRect; clippedDstRect = *dstRect; if (!srcToDstMatrix.setRectToRect(*srcRect, *dstRect, SkMatrix::kFill_ScaleToFit)) { return; } } } else { clippedSrcRect = srcBounds; if (dstRect) { clippedDstRect = *dstRect; if (!srcToDstMatrix.setRectToRect(srcBounds, *dstRect, SkMatrix::kFill_ScaleToFit)) { return; } } else { clippedDstRect = srcBounds; srcToDstMatrix.reset(); } } // Now that we have both the view and srcToDst matrices, log our scale factor. LogDrawScaleFactor(SkMatrix::Concat(viewMatrix, srcToDstMatrix), paint.getFilterQuality()); this->drawTextureProducerImpl(producer, clippedSrcRect, clippedDstRect, constraint, viewMatrix, srcToDstMatrix, clip, paint); }
// This does a lot of computation to resample only the portion of the bitmap // that will only be drawn. This is critical for performance since when we are // scrolling, for example, we are only drawing a small strip of the image. // Resampling the whole image every time is very slow, so this speeds up things // dramatically. // // Note: this code is only used when the canvas transformation is limited to // scaling or translation. void NativeImageSkia::drawResampledBitmap(GraphicsContext* context, SkPaint& paint, const SkRect& srcRect, const SkRect& destRect) const { TRACE_EVENT0("skia", "drawResampledBitmap"); // We want to scale |destRect| with transformation in the canvas to obtain // the final scale. The final scale is a combination of scale transform // in canvas and explicit scaling (srcRect and destRect). SkRect screenRect; context->getTotalMatrix().mapRect(&screenRect, destRect); float realScaleX = screenRect.width() / srcRect.width(); float realScaleY = screenRect.height() / srcRect.height(); // This part of code limits scaling only to visible portion in the SkRect destRectVisibleSubset; if (!context->canvas()->getClipBounds(&destRectVisibleSubset)) return; // ClipRectToCanvas often overshoots, resulting in a larger region than our // original destRect. Intersecting gets us back inside. if (!destRectVisibleSubset.intersect(destRect)) return; // Nothing visible in destRect. // Find the corresponding rect in the source image. SkMatrix destToSrcTransform; SkRect srcRectVisibleSubset; destToSrcTransform.setRectToRect(destRect, srcRect, SkMatrix::kFill_ScaleToFit); destToSrcTransform.mapRect(&srcRectVisibleSubset, destRectVisibleSubset); SkRect scaledSrcRect; SkBitmap scaledImageFragment = extractScaledImageFragment(srcRectVisibleSubset, realScaleX, realScaleY, &scaledSrcRect); context->drawBitmapRect(scaledImageFragment, &scaledSrcRect, destRectVisibleSubset, &paint); }
// This function is used to scale an image and extract a scaled fragment. // // ALGORITHM // // Because the scaled image size has to be integers, we approximate the real // scale with the following formula (only X direction is shown): // // scaledImageWidth = round(scaleX * imageRect.width()) // approximateScaleX = scaledImageWidth / imageRect.width() // // With this method we maintain a constant scale factor among fragments in // the scaled image. This allows fragments to stitch together to form the // full scaled image. The downside is there will be a small difference // between |scaleX| and |approximateScaleX|. // // A scaled image fragment is identified by: // // - Scaled image size // - Scaled image fragment rectangle (IntRect) // // Scaled image size has been determined and the next step is to compute the // rectangle for the scaled image fragment which needs to be an IntRect. // // scaledSrcRect = srcRect * (approximateScaleX, approximateScaleY) // enclosingScaledSrcRect = enclosingIntRect(scaledSrcRect) // // Finally we extract the scaled image fragment using // (scaledImageSize, enclosingScaledSrcRect). // SkBitmap NativeImageSkia::extractScaledImageFragment(const SkRect& srcRect, float scaleX, float scaleY, SkRect* scaledSrcRect) const { SkISize imageSize = SkISize::Make(bitmap().width(), bitmap().height()); SkISize scaledImageSize = SkISize::Make(clampToInteger(roundf(imageSize.width() * scaleX)), clampToInteger(roundf(imageSize.height() * scaleY))); SkRect imageRect = SkRect::MakeWH(imageSize.width(), imageSize.height()); SkRect scaledImageRect = SkRect::MakeWH(scaledImageSize.width(), scaledImageSize.height()); SkMatrix scaleTransform; scaleTransform.setRectToRect(imageRect, scaledImageRect, SkMatrix::kFill_ScaleToFit); scaleTransform.mapRect(scaledSrcRect, srcRect); bool ok = scaledSrcRect->intersect(scaledImageRect); ASSERT_UNUSED(ok, ok); SkIRect enclosingScaledSrcRect = enclosingIntRect(*scaledSrcRect); // |enclosingScaledSrcRect| can be larger than |scaledImageSize| because // of float inaccuracy so clip to get inside. ok = enclosingScaledSrcRect.intersect(SkIRect::MakeSize(scaledImageSize)); ASSERT_UNUSED(ok, ok); // scaledSrcRect is relative to the pixel snapped fragment we're extracting. scaledSrcRect->offset(-enclosingScaledSrcRect.x(), -enclosingScaledSrcRect.y()); return resizedBitmap(scaledImageSize, enclosingScaledSrcRect); }
CameraView() { fRX = fRY = fRZ = 0; fShaderIndex = 0; fFrontFace = false; for (int i = 0;; i++) { SkString str; str.printf("/skimages/elephant%d.jpeg", i); SkBitmap bm; if (SkImageDecoder::DecodeFile(str.c_str(), &bm)) { SkShader* s = SkShader::CreateBitmapShader(bm, SkShader::kClamp_TileMode, SkShader::kClamp_TileMode); SkRect src = { 0, 0, SkIntToScalar(bm.width()), SkIntToScalar(bm.height()) }; SkRect dst = { -150, -150, 150, 150 }; SkMatrix matrix; matrix.setRectToRect(src, dst, SkMatrix::kFill_ScaleToFit); s->setLocalMatrix(matrix); *fShaders.append() = s; } else { break; } } this->setBGColor(0xFFDDDDDD); }
void SkImagePrivDrawPicture(SkCanvas* canvas, SkPicture* picture, const SkRect* src, const SkRect& dst, const SkPaint* paint) { int saveCount = canvas->getSaveCount(); SkMatrix matrix; SkRect tmpSrc; if (NULL != src) { tmpSrc = *src; } else { tmpSrc.set(0, 0, SkIntToScalar(picture->width()), SkIntToScalar(picture->height())); } matrix.setRectToRect(tmpSrc, dst, SkMatrix::kFill_ScaleToFit); if (paint && needs_layer(*paint)) { canvas->saveLayer(&dst, paint); } else { canvas->save(); } canvas->concat(matrix); if (!paint || !needs_layer(*paint)) { canvas->clipRect(tmpSrc); } canvas->drawPicture(*picture); canvas->restoreToCount(saveCount); }
void DrawTargetSkia::DrawSurface(SourceSurface *aSurface, const Rect &aDest, const Rect &aSource, const DrawSurfaceOptions &aSurfOptions, const DrawOptions &aOptions) { if (aSurface->GetType() != SURFACE_SKIA) { return; } if (aSource.IsEmpty()) { return; } MarkChanged(); SkRect destRect = RectToSkRect(aDest); SkRect sourceRect = RectToSkRect(aSource); SkMatrix matrix; matrix.setRectToRect(sourceRect, destRect, SkMatrix::kFill_ScaleToFit); const SkBitmap& bitmap = static_cast<SourceSurfaceSkia*>(aSurface)->GetBitmap(); AutoPaintSetup paint(mCanvas.get(), aOptions); SkShader *shader = SkShader::CreateBitmapShader(bitmap, SkShader::kClamp_TileMode, SkShader::kClamp_TileMode); shader->setLocalMatrix(matrix); SkSafeUnref(paint.mPaint.setShader(shader)); if (aSurfOptions.mFilter != FILTER_LINEAR) { paint.mPaint.setFilterBitmap(false); } mCanvas->drawRect(destRect, paint.mPaint); }
static bool oval_contains(const SkRect& r, SkScalar x, SkScalar y) { SkMatrix m; m.setRectToRect(r, gUnitSquare, SkMatrix::kFill_ScaleToFit); SkPoint pt; m.mapXY(x, y, &pt); return pt.lengthSqd() <= 1; }
static void test_huge_stroke(SkCanvas* canvas) { SkRect srcR = { 0, 0, 72000, 54000 }; SkRect dstR = { 0, 0, 640, 480 }; SkPath path; path.moveTo(17600, 8000); path.lineTo(52800, 8000); path.lineTo(52800, 41600); path.lineTo(17600, 41600); path.close(); SkPaint paint; paint.setAntiAlias(true); paint.setStrokeWidth(8000); paint.setStrokeMiter(10); paint.setStrokeCap(SkPaint::kButt_Cap); paint.setStrokeJoin(SkPaint::kRound_Join); paint.setStyle(SkPaint::kStroke_Style); SkMatrix matrix; matrix.setRectToRect(srcR, dstR, SkMatrix::kCenter_ScaleToFit); canvas->concat(matrix); canvas->drawPath(path, paint); }
/** * Method which handles native redrawing screen */ void JNISDLVideoDriverListener::updateScreen(SkBitmap* bitmap) { SkBitmap screen; Surface::SurfaceInfo surfaceInfo; if (!mSurface || !mSurface->isValid()) { __android_log_print(ANDROID_LOG_INFO, LOG_TAG, "nativeSurface wasn't valid"); return; } if (mSurface->lock(&surfaceInfo) < 0) { __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, "Failed to lock surface"); return; } //__android_log_print(ANDROID_LOG_ERROR, LOG_TAG, "updating screen: %ix%i", surfaceInfo.w, surfaceInfo.h); //bitmap which is drawed on android surfaceview SDLVideoDriver::setBitmapConfig(&screen, surfaceInfo.format, surfaceInfo.w, surfaceInfo.h); screen.setPixels(surfaceInfo.bits); SkCanvas canvas(screen); SkRect surface_sdl; SkRect surface_android; SkMatrix matrix; surface_android.set(0, 0, screen.width(), screen.height()); surface_sdl.set(0, 0, bitmap->width(), bitmap->height()); matrix.setRectToRect(surface_sdl, surface_android, SkMatrix::kFill_ScaleToFit); canvas.drawBitmapMatrix(*bitmap, matrix); if (mSurface->unlockAndPost() < 0) { __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, "Failed to unlock surface"); } }
bool SkHitTestPath(const SkPath& path, SkRect& target, bool hires) { if (target.isEmpty()) { return false; } bool isInverse = path.isInverseFillType(); if (path.isEmpty()) { return isInverse; } SkRect bounds = path.getBounds(); bool sects = SkRect::Intersects(target, bounds); if (isInverse) { if (!sects) { return true; } } else { if (!sects) { return false; } if (target.contains(bounds)) { return true; } } SkPath devPath; const SkPath* pathPtr; SkRect devTarget; if (hires) { const SkScalar coordLimit = SkIntToScalar(16384); const SkRect limit = { 0, 0, coordLimit, coordLimit }; SkMatrix matrix; matrix.setRectToRect(bounds, limit, SkMatrix::kFill_ScaleToFit); path.transform(matrix, &devPath); matrix.mapRect(&devTarget, target); pathPtr = &devPath; } else { devTarget = target; pathPtr = &path; } SkIRect iTarget; devTarget.round(&iTarget); if (iTarget.isEmpty()) { iTarget.fLeft = SkScalarFloorToInt(devTarget.fLeft); iTarget.fTop = SkScalarFloorToInt(devTarget.fTop); iTarget.fRight = iTarget.fLeft + 1; iTarget.fBottom = iTarget.fTop + 1; } SkRegion clip(iTarget); SkRegion rgn; return rgn.setPath(*pathPtr, clip) ^ isInverse; }
static jboolean setRectToRect(JNIEnv* env, jobject clazz, jlong matrixHandle, jobject src, jobject dst, jint stfHandle) { SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixHandle); SkMatrix::ScaleToFit stf = static_cast<SkMatrix::ScaleToFit>(stfHandle); SkRect src_; GraphicsJNI::jrectf_to_rect(env, src, &src_); SkRect dst_; GraphicsJNI::jrectf_to_rect(env, dst, &dst_); return matrix->setRectToRect(src_, dst_, stf) ? JNI_TRUE : JNI_FALSE; }
virtual void onDraw(SkCanvas* canvas) { SkRect dst = SkRect::MakeWH(canvas->getDevice()->width(), canvas->getDevice()->height()); SkRect src = SkRect::MakeWH(640, 480); SkMatrix matrix; matrix.setRectToRect(src, dst, SkMatrix::kCenter_ScaleToFit); canvas->concat(matrix); fProc(canvas, fShowGL, fFlags); }
std::unique_ptr<GrDrawOp> MakeAAFillWithLocalRect(GrPaint&& paint, const SkMatrix& viewMatrix, const SkRect& rect, const SkRect& localRect) { if (!view_matrix_ok_for_aa_fill_rect(viewMatrix)) { return nullptr; } SkRect devRect; viewMatrix.mapRect(&devRect, rect); SkMatrix localMatrix; if (!localMatrix.setRectToRect(rect, localRect, SkMatrix::kFill_ScaleToFit)) { return nullptr; } return AAFillRectOp::Make(std::move(paint), viewMatrix, rect, devRect, &localMatrix, nullptr); }
Boolean Matrix::NativeSetRectToRect( /* [in] */ Int64 nObj, /* [in] */ IRectF* src, /* [in] */ IRectF* dst, /* [in] */ Int32 stfHandle) { SkMatrix* matrix = reinterpret_cast<SkMatrix*>(nObj); SkMatrix::ScaleToFit stf = static_cast<SkMatrix::ScaleToFit>(stfHandle); SkRect src_; GraphicsNative::IRectF2SkRect(src, &src_); SkRect dst_; GraphicsNative::IRectF2SkRect(dst, &dst_); return matrix->setRectToRect(src_, dst_, stf) ? TRUE : FALSE; }
void ASurface_scaleToFullScreen(ASurface* aSurface, AndroidSurfaceInfo* src, AndroidSurfaceInfo* dst) { SkBitmap srcBitmap; SkBitmap dstBitmap; SkMatrix matrix; initBitmap(srcBitmap, src); initBitmap(dstBitmap, dst); matrix.setRectToRect(SkRect::MakeWH(srcBitmap.width(), srcBitmap.height()), SkRect::MakeWH(dstBitmap.width(), dstBitmap.height()), SkMatrix::kCenter_ScaleToFit); aSurface->canvas->setBitmapDevice(dstBitmap); aSurface->canvas->drawBitmapMatrix(srcBitmap, matrix); return; }
static void copybits(SkCanvas* canvas, const SkBitmap& bm, const SkRect& dst, const SkPaint& paint) { SkRect src; SkMatrix matrix; src.set(0, 0, SkIntToScalar(bm.width()), SkIntToScalar(bm.height())); if (matrix.setRectToRect(src, dst)) { SkPaint p(paint); SkShader* shader = SkShader::CreateBitmapShader(bm, false, SkPaint::kNo_FilterType, SkShader::kClamp_TileMode); p.setShader(shader)->unref(); shader->setLocalMatrix(matrix); canvas->drawRect(dst, p); } }
// FIXME: Remove this code when SkCanvas accepts SkRect as source rectangle. // See crbug.com/117597 for background. // // WebKit wants to draw a sub-rectangle (FloatRect) in a bitmap and scale it to // another FloatRect. However Skia only allows bitmap to be addressed by a // IntRect. This function computes the appropriate IntRect that encloses the // source rectangle and the corresponding enclosing destination rectangle, // while maintaining the scale factor. // // |srcRect| is the source rectangle in the bitmap. Return true if fancy // alignment is required. User of this function needs to clip to |dstRect|. // Return false if clipping is not needed. // // |dstRect| is the input rectangle that |srcRect| is scaled to. // // |outSrcRect| and |outDstRect| are the corresponding output rectangles. // // ALGORITHM // // The objective is to (a) find an enclosing IntRect for the source rectangle // and (b) the corresponding FloatRect in destination space. // // These are the steps performed: // // 1. IntRect enclosingSrcRect = enclosingIntRect(srcRect) // // Compute the enclosing IntRect for |srcRect|. This ensures the bitmap // image is addressed with integer boundaries. // // 2. FloatRect enclosingDestRect = mapSrcToDest(enclosingSrcRect) // // Map the enclosing source rectangle to destination coordinate space. // // The output will be enclosingSrcRect and enclosingDestRect from the // algorithm above. static bool computeBitmapDrawRects(const SkISize& bitmapSize, const SkRect& srcRect, const SkRect& dstRect, SkIRect* outSrcRect, SkRect* outDstRect) { if (areBoundariesIntegerAligned(srcRect)) { *outSrcRect = roundedIntRect(srcRect); *outDstRect = dstRect; return false; } SkIRect bitmapRect = SkIRect::MakeSize(bitmapSize); SkIRect enclosingSrcRect = enclosingIntRect(srcRect); enclosingSrcRect.intersect(bitmapRect); // Clip to bitmap rectangle. SkRect enclosingDstRect; enclosingDstRect.set(enclosingSrcRect); SkMatrix transform; transform.setRectToRect(srcRect, dstRect, SkMatrix::kFill_ScaleToFit); transform.mapRect(&enclosingDstRect); *outSrcRect = enclosingSrcRect; *outDstRect = enclosingDstRect; return true; }
// This function is used to scale an image and extract a scaled fragment. // // ALGORITHM // // Because the scaled image size has to be integers, we approximate the real // scale with the following formula (only X direction is shown): // // scaledImageWidth = round(scaleX * imageRect.width()) // approximateScaleX = scaledImageWidth / imageRect.width() // // With this method we maintain a constant scale factor among fragments in // the scaled image. This allows fragments to stitch together to form the // full scaled image. The downside is there will be a small difference // between |scaleX| and |approximateScaleX|. // // A scaled image fragment is identified by: // // - Scaled image size // - Scaled image fragment rectangle (IntRect) // // Scaled image size has been determined and the next step is to compute the // rectangle for the scaled image fragment which needs to be an IntRect. // // scaledSrcRect = srcRect * (approximateScaleX, approximateScaleY) // enclosingScaledSrcRect = enclosingIntRect(scaledSrcRect) // // Finally we extract the scaled image fragment using // (scaledImageSize, enclosingScaledSrcRect). // static SkBitmap extractScaledImageFragment(const NativeImageSkia& bitmap, const SkRect& srcRect, float scaleX, float scaleY, SkRect* scaledSrcRect, SkIRect* enclosingScaledSrcRect) { SkISize imageSize = SkISize::Make(bitmap.bitmap().width(), bitmap.bitmap().height()); SkISize scaledImageSize = SkISize::Make(clampToInteger(roundf(imageSize.width() * scaleX)), clampToInteger(roundf(imageSize.height() * scaleY))); SkRect imageRect = SkRect::MakeWH(imageSize.width(), imageSize.height()); SkRect scaledImageRect = SkRect::MakeWH(scaledImageSize.width(), scaledImageSize.height()); SkMatrix scaleTransform; scaleTransform.setRectToRect(imageRect, scaledImageRect, SkMatrix::kFill_ScaleToFit); scaleTransform.mapRect(scaledSrcRect, srcRect); scaledSrcRect->intersect(scaledImageRect); *enclosingScaledSrcRect = enclosingIntRect(*scaledSrcRect); // |enclosingScaledSrcRect| can be larger than |scaledImageSize| because // of float inaccuracy so clip to get inside. enclosingScaledSrcRect->intersect(SkIRect::MakeSize(scaledImageSize)); return bitmap.resizedBitmap(scaledImageSize, *enclosingScaledSrcRect); }
void SkSVGDevice::drawBitmapRect(const SkDraw& draw, const SkBitmap& bm, const SkRect* srcOrNull, const SkRect& dst, const SkPaint& paint, SkCanvas::SrcRectConstraint) { SkMatrix adjustedMatrix; adjustedMatrix.setRectToRect(srcOrNull ? *srcOrNull : SkRect::Make(bm.bounds()), dst, SkMatrix::kFill_ScaleToFit); adjustedMatrix.postConcat(*draw.fMatrix); SkDraw adjustedDraw(draw); adjustedDraw.fMatrix = &adjustedMatrix; SkClipStack adjustedClipStack; if (srcOrNull && *srcOrNull != SkRect::Make(bm.bounds())) { adjustedClipStack = *draw.fClipStack; adjustedClipStack.clipRect(dst, *draw.fMatrix, SkCanvas::kIntersect_Op, paint.isAntiAlias()); adjustedDraw.fClipStack = &adjustedClipStack; } drawBitmapCommon(adjustedDraw, bm, paint); }
void SkSVGDevice::drawBitmapRect(const SkDraw& draw, const SkBitmap& bm, const SkRect* srcOrNull, const SkRect& dst, const SkPaint& paint, SK_VIRTUAL_CONSTRAINT_TYPE) { SkMatrix adjustedMatrix; adjustedMatrix.setRectToRect(srcOrNull ? *srcOrNull : SkRect::Make(bm.bounds()), dst, SkMatrix::kFill_ScaleToFit); adjustedMatrix.postConcat(*draw.fMatrix); SkDraw adjustedDraw(draw); adjustedDraw.fMatrix = &adjustedMatrix; SkClipStack adjustedClipStack; if (srcOrNull && *srcOrNull != SkRect::Make(bm.bounds())) { SkRect devClipRect; draw.fMatrix->mapRect(&devClipRect, dst); adjustedClipStack = *draw.fClipStack; adjustedClipStack.clipDevRect(devClipRect, SkRegion::kIntersect_Op, paint.isAntiAlias()); adjustedDraw.fClipStack = &adjustedClipStack; } drawBitmapCommon(adjustedDraw, bm, paint); }
void ASurface_scaleToFullScreen_skia(ASurface* aSurface, AndroidSurfaceInfo* src, AndroidSurfaceInfo* dst, size_t size) { SkBitmap dstBitmap; SkMatrix matrix; void *pixel = NULL; int swidth = 0; int sheight = 0; int i, width, height, buf_size; width = 0; height = 0; struct jpeg_ext *ext = (struct jpeg_ext *)src->bits; char *real_jpeg = (char *)src->bits + sizeof(struct jpeg_ext); LOGD("```````````````````````````````````"); LOGD("%s: %d\n", __func__, __LINE__); if(ext->fragment_num == 1) { if (ASurface_decode(&srcBitmap, real_jpeg, size, &swidth, &sheight)) { LOGE("decode error\n"); return; } LOGD("swidth = %d, sheight = %d\n", swidth, sheight); } else { SkBitmap sktemp[6]; width = 0; height = 0; for(i = 0; i < ext->fragment_num; i++) { if (ASurface_decode(&sktemp[i], real_jpeg + ext->fragment[i].offset, ext->fragment[i].size, &swidth, &sheight)) { LOGE("decode error\n"); return; } width = sktemp[i].width(); height += sktemp[i].height(); } srcBitmap.setConfig(SkBitmap::kRGB_565_Config, width, height); LOGD("%s: real height = %d width = %d\n", __func__, height, width); buf_size = width * height * 2; pixel = malloc(buf_size); if(!pixel) return; width = height = 0; for(i = 0; i < ext->fragment_num; i++) { void *frag_buf; width = sktemp[i].width(); frag_buf = sktemp[i].getPixels(); memcpy((char *)pixel + (height * width * 2), frag_buf, sktemp[i].getSize()); height += sktemp[i].height(); } srcBitmap.setPixels(pixel); } if(dst->w != dst->s) { void *buf_temp; int i, pixel_size; SkBitmap temp; if(dst->format == ANDROID_PIXEL_FORMAT_RGB_565) pixel_size = 2; else pixel_size = 4; buf_temp = malloc(dst->w * dst->h * pixel_size); if(!buf_temp) return; temp.setConfig(convertPixelFormat(dst->format), dst->w, dst->h); temp.setPixels(buf_temp); matrix.setRectToRect(SkRect::MakeWH(srcBitmap.width(), srcBitmap.height()), SkRect::MakeWH(temp.width(), temp.height()), SkMatrix::kFill_ScaleToFit); for(i = 0; i < (int)dst->h; i++) memcpy((char *)dst->bits + i * dst->s * pixel_size, (char *)buf_temp + i * dst->w * pixel_size, dst->w * pixel_size); dstBitmap.setConfig(convertPixelFormat(dst->format), dst->s, dst->h); dstBitmap.setPixels(dst->bits); aSurface->canvas->setBitmapDevice(dstBitmap); aSurface->canvas->drawBitmapMatrix(srcBitmap, matrix); free(buf_temp); } else { #ifndef SKIA_DECODE srcBitmap.setConfig(convertPixelFormat(ANDROID_PIXEL_FORMAT_RGBA_8888), swidth, sheight); srcBitmap.setPixels(outBuf2); #else #endif initBitmap(dstBitmap, dst); #if 0 /*original*/ LOGD("Jerry catch Tom: SKIA : g_clear_screen_client = %d\n", g_clear_screen_client); matrix.setRectToRect(SkRect::MakeWH(srcBitmap.width(), srcBitmap.height()), SkRect::MakeWH(dstBitmap.width(), dstBitmap.height()), SkMatrix::kFill_ScaleToFit); #else /* clear screen */ //memset(srcBitmap.getPixels(), 0, width * height * 2); LOGD("Hi, Tom, I'm Jerry!\n"); LOGD("dst->w = %d, dst->h = %d", dst->w, dst->h); memset(dst->bits, 0, dst->w * dst->h * 2); if (g_clear_screen_client) { matrix.setRectToRect(SkRect::MakeWH(srcBitmap.width(), srcBitmap.height()), SkRect::MakeWH(dstBitmap.width(), dstBitmap.height()), SkMatrix::kFill_ScaleToFit); aSurface->canvas->setBitmapDevice(dstBitmap); aSurface->canvas->drawBitmapMatrix(srcBitmap, matrix); } else { matrix.setRectToRect(SkRect::MakeWH(srcBitmap.width(), srcBitmap.height()), SkRect::MakeWH(dstBitmap.width(), dstBitmap.height()), SkMatrix::kCenter_ScaleToFit); aSurface->canvas->setBitmapDevice(dstBitmap); aSurface->canvas->drawBitmapMatrix(srcBitmap, matrix); } #endif } if(ext->fragment_num > 1) { free(pixel); srcBitmap.setPixels(NULL); } return; }
static void draw_vector_logo(SkCanvas* canvas, const SkRect& viewBox) { constexpr char kSkiaStr[] = "SKIA"; constexpr SkScalar kGradientPad = .1f; constexpr SkScalar kVerticalSpacing = 0.25f; constexpr SkScalar kAccentScale = 1.20f; SkPaint paint; paint.setAntiAlias(true); paint.setSubpixelText(true); paint.setFakeBoldText(true); sk_tool_utils::set_portable_typeface(&paint); SkPath path; SkRect iBox, skiBox, skiaBox; paint.getTextPath("SKI", 3, 0, 0, &path); TightBounds(path, &skiBox); paint.getTextPath("I", 1, 0, 0, &path); TightBounds(path, &iBox); iBox.offsetTo(skiBox.fRight - iBox.width(), iBox.fTop); const size_t textLen = strlen(kSkiaStr); paint.getTextPath(kSkiaStr, textLen, 0, 0, &path); TightBounds(path, &skiaBox); skiaBox.outset(0, 2 * iBox.width() * (kVerticalSpacing + 1)); const SkScalar accentSize = iBox.width() * kAccentScale; const SkScalar underlineY = iBox.bottom() + (kVerticalSpacing + SkScalarSqrt(3) / 2) * accentSize; SkMatrix m; m.setRectToRect(skiaBox, viewBox, SkMatrix::kFill_ScaleToFit); SkAutoCanvasRestore acr(canvas, true); canvas->concat(m); canvas->drawCircle(iBox.centerX(), iBox.y() - (0.5f + kVerticalSpacing) * accentSize, accentSize / 2, paint); path.reset(); path.moveTo(iBox.centerX() - accentSize / 2, iBox.bottom() + kVerticalSpacing * accentSize); path.rLineTo(accentSize, 0); path.lineTo(iBox.centerX(), underlineY); canvas->drawPath(path, paint); SkRect underlineRect = SkRect::MakeLTRB(iBox.centerX() - iBox.width() * accentSize * 3, underlineY, iBox.centerX(), underlineY + accentSize / 10); const SkPoint pts1[] = { SkPoint::Make(underlineRect.x(), 0), SkPoint::Make(iBox.centerX(), 0) }; const SkScalar pos1[] = { 0, 0.75f }; const SkColor colors1[] = { SK_ColorTRANSPARENT, SK_ColorBLACK }; SkASSERT(SK_ARRAY_COUNT(pos1) == SK_ARRAY_COUNT(colors1)); paint.setShader(SkGradientShader::MakeLinear(pts1, colors1, pos1, SK_ARRAY_COUNT(pos1), SkShader::kClamp_TileMode)); canvas->drawRect(underlineRect, paint); const SkPoint pts2[] = { SkPoint::Make(iBox.x() - iBox.width() * kGradientPad, 0), SkPoint::Make(iBox.right() + iBox.width() * kGradientPad, 0) }; const SkScalar pos2[] = { 0, .01f, 1.0f/3, 1.0f/3, 2.0f/3, 2.0f/3, .99f, 1 }; const SkColor colors2[] = { SK_ColorBLACK, 0xffca5139, 0xffca5139, 0xff8dbd53, 0xff8dbd53, 0xff5460a5, 0xff5460a5, SK_ColorBLACK }; SkASSERT(SK_ARRAY_COUNT(pos2) == SK_ARRAY_COUNT(colors2)); paint.setShader(SkGradientShader::MakeLinear(pts2, colors2, pos2, SK_ARRAY_COUNT(pos2), SkShader::kClamp_TileMode)); canvas->drawText(kSkiaStr, textLen, 0, 0, paint); }
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); }
bool SkBicubicImageFilter::onFilterImage(Proxy* proxy, const SkBitmap& source, const SkMatrix& matrix, SkBitmap* result, SkIPoint* loc) { SkBitmap src = source; if (getInput(0) && !getInput(0)->filterImage(proxy, source, matrix, &src, loc)) { return false; } if (src.config() != SkBitmap::kARGB_8888_Config) { return false; } SkAutoLockPixels alp(src); if (!src.getPixels()) { return false; } SkRect dstRect = SkRect::MakeWH(SkScalarMul(SkIntToScalar(src.width()), fScale.fWidth), SkScalarMul(SkIntToScalar(src.height()), fScale.fHeight)); SkIRect dstIRect; dstRect.roundOut(&dstIRect); result->setConfig(src.config(), dstIRect.width(), dstIRect.height()); result->allocPixels(); if (!result->getPixels()) { return false; } SkRect srcRect; src.getBounds(&srcRect); SkMatrix inverse; inverse.setRectToRect(dstRect, srcRect, SkMatrix::kFill_ScaleToFit); inverse.postTranslate(SkFloatToScalar(-0.5f), SkFloatToScalar(-0.5f)); for (int y = dstIRect.fTop; y < dstIRect.fBottom; ++y) { SkPMColor* dptr = result->getAddr32(dstIRect.fLeft, y); for (int x = dstIRect.fLeft; x < dstIRect.fRight; ++x) { SkPoint srcPt, dstPt = SkPoint::Make(SkIntToScalar(x), SkIntToScalar(y)); inverse.mapPoints(&srcPt, &dstPt, 1); SkScalar fractx = srcPt.fX - SkScalarFloorToScalar(srcPt.fX); SkScalar fracty = srcPt.fY - SkScalarFloorToScalar(srcPt.fY); int sx = SkScalarFloorToInt(srcPt.fX); int sy = SkScalarFloorToInt(srcPt.fY); int x0 = SkClampMax(sx - 1, src.width() - 1); int x1 = SkClampMax(sx , src.width() - 1); int x2 = SkClampMax(sx + 1, src.width() - 1); int x3 = SkClampMax(sx + 2, src.width() - 1); int y0 = SkClampMax(sy - 1, src.height() - 1); int y1 = SkClampMax(sy , src.height() - 1); int y2 = SkClampMax(sy + 1, src.height() - 1); int y3 = SkClampMax(sy + 2, src.height() - 1); SkPMColor s00 = *src.getAddr32(x0, y0); SkPMColor s10 = *src.getAddr32(x1, y0); SkPMColor s20 = *src.getAddr32(x2, y0); SkPMColor s30 = *src.getAddr32(x3, y0); SkPMColor s0 = cubicBlend(fCoefficients, fractx, s00, s10, s20, s30); SkPMColor s01 = *src.getAddr32(x0, y1); SkPMColor s11 = *src.getAddr32(x1, y1); SkPMColor s21 = *src.getAddr32(x2, y1); SkPMColor s31 = *src.getAddr32(x3, y1); SkPMColor s1 = cubicBlend(fCoefficients, fractx, s01, s11, s21, s31); SkPMColor s02 = *src.getAddr32(x0, y2); SkPMColor s12 = *src.getAddr32(x1, y2); SkPMColor s22 = *src.getAddr32(x2, y2); SkPMColor s32 = *src.getAddr32(x3, y2); SkPMColor s2 = cubicBlend(fCoefficients, fractx, s02, s12, s22, s32); SkPMColor s03 = *src.getAddr32(x0, y3); SkPMColor s13 = *src.getAddr32(x1, y3); SkPMColor s23 = *src.getAddr32(x2, y3); SkPMColor s33 = *src.getAddr32(x3, y3); SkPMColor s3 = cubicBlend(fCoefficients, fractx, s03, s13, s23, s33); *dptr++ = cubicBlend(fCoefficients, fracty, s0, s1, s2, s3); } } return true; }
void SkBitmapDevice::drawBitmapRect(const SkDraw& draw, const SkBitmap& bitmap, const SkRect* src, const SkRect& dst, const SkPaint& paint, SkCanvas::DrawBitmapRectFlags flags) { 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); 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; } // since we may need to clamp to the borders of the src rect within // the bitmap, we extract a subset. SkIRect srcIR; tmpSrc.roundOut(&srcIR); 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. this->drawBitmap(draw, *bitmapPtr, matrix, paint); return; } // construct a shader, so we can call drawRect with the dst SkShader* s = SkShader::CreateBitmapShader(*bitmapPtr, SkShader::kClamp_TileMode, SkShader::kClamp_TileMode, &matrix); if (NULL == s) { return; } SkPaint paintWithShader(paint); paintWithShader.setStyle(SkPaint::kFill_Style); paintWithShader.setShader(s)->unref(); // Call ourself, in case the subclass wanted to share this setup code // but handle the drawRect code themselves. this->drawRect(draw, *dstPtr, paintWithShader); }
static void make_path_star(SkPath* path, const SkRect& bounds) { make_unit_star(path, 5); SkMatrix matrix; matrix.setRectToRect(path->getBounds(), bounds, SkMatrix::kCenter_ScaleToFit); path->transform(matrix); }
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 = SkShader::MakeBitmapShader(normals, SkShader::kClamp_TileMode, SkShader::kClamp_TileMode, &matrix); sk_sp<SkNormalSource> normalSource = SkNormalSource::MakeFromNormalMap(std::move(normalMap), ctm); sk_sp<SkShader> diffuseShader = SkShader::MakeBitmapShader(diffuse, SkShader::kClamp_TileMode, SkShader::kClamp_TileMode, &matrix); sk_sp<SkShader> lightingShader = SkLightingShader::Make(diffuseShader, normalSource, fLights); SkAutoTUnref<SkShader>(TestFlattenableSerialization(lightingShader.get(), true, reporter)); lightingShader = SkLightingShader::Make(std::move(diffuseShader), nullptr, fLights); SkAutoTUnref<SkShader>(TestFlattenableSerialization(lightingShader.get(), true, reporter)); lightingShader = SkLightingShader::Make(nullptr, std::move(normalSource), fLights); SkAutoTUnref<SkShader>(TestFlattenableSerialization(lightingShader.get(), true, reporter)); lightingShader = SkLightingShader::Make(nullptr, nullptr, fLights); SkAutoTUnref<SkShader>(TestFlattenableSerialization(lightingShader.get(), true, reporter)); } // Test NormalBevelSource serialization { sk_sp<SkNormalSource> bevelSource = SkNormalSource::MakeBevel( SkNormalSource::BevelType::kLinear, 2.0f, 5.0f); SkAutoTUnref<SkNormalSource>(TestFlattenableSerialization(bevelSource.get(), true, reporter)); // TODO test equality? } }