SkGpuDevice_drawTexture.cpp 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596
  1. /*
  2. * Copyright 2015 Google Inc.
  3. *
  4. * Use of this source code is governed by a BSD-style license that can be
  5. * found in the LICENSE file.
  6. */
  7. #include "src/gpu/SkGpuDevice.h"
  8. #include "include/core/SkYUVAIndex.h"
  9. #include "src/core/SkDraw.h"
  10. #include "src/core/SkMaskFilterBase.h"
  11. #include "src/gpu/GrBitmapTextureMaker.h"
  12. #include "src/gpu/GrBlurUtils.h"
  13. #include "src/gpu/GrCaps.h"
  14. #include "src/gpu/GrColorSpaceXform.h"
  15. #include "src/gpu/GrImageTextureMaker.h"
  16. #include "src/gpu/GrRenderTargetContext.h"
  17. #include "src/gpu/GrStyle.h"
  18. #include "src/gpu/GrTextureAdjuster.h"
  19. #include "src/gpu/GrTextureMaker.h"
  20. #include "src/gpu/SkGr.h"
  21. #include "src/gpu/effects/GrBicubicEffect.h"
  22. #include "src/gpu/effects/GrTextureDomain.h"
  23. #include "src/gpu/effects/generated/GrSimpleTextureEffect.h"
  24. #include "src/gpu/geometry/GrShape.h"
  25. #include "src/image/SkImage_Base.h"
  26. namespace {
  27. static inline bool use_shader(bool textureIsAlphaOnly, const SkPaint& paint) {
  28. return textureIsAlphaOnly && paint.getShader();
  29. }
  30. //////////////////////////////////////////////////////////////////////////////
  31. // Helper functions for dropping src rect constraint in bilerp mode.
  32. static const SkScalar kColorBleedTolerance = 0.001f;
  33. static bool has_aligned_samples(const SkRect& srcRect, const SkRect& transformedRect) {
  34. // detect pixel disalignment
  35. if (SkScalarAbs(SkScalarRoundToScalar(transformedRect.left()) - transformedRect.left()) < kColorBleedTolerance &&
  36. SkScalarAbs(SkScalarRoundToScalar(transformedRect.top()) - transformedRect.top()) < kColorBleedTolerance &&
  37. SkScalarAbs(transformedRect.width() - srcRect.width()) < kColorBleedTolerance &&
  38. SkScalarAbs(transformedRect.height() - srcRect.height()) < kColorBleedTolerance) {
  39. return true;
  40. }
  41. return false;
  42. }
  43. static bool may_color_bleed(const SkRect& srcRect,
  44. const SkRect& transformedRect,
  45. const SkMatrix& m,
  46. int numSamples) {
  47. // Only gets called if has_aligned_samples returned false.
  48. // So we can assume that sampling is axis aligned but not texel aligned.
  49. SkASSERT(!has_aligned_samples(srcRect, transformedRect));
  50. SkRect innerSrcRect(srcRect), innerTransformedRect, outerTransformedRect(transformedRect);
  51. if (numSamples > 1) {
  52. innerSrcRect.inset(SK_Scalar1, SK_Scalar1);
  53. } else {
  54. innerSrcRect.inset(SK_ScalarHalf, SK_ScalarHalf);
  55. }
  56. m.mapRect(&innerTransformedRect, innerSrcRect);
  57. // The gap between outerTransformedRect and innerTransformedRect
  58. // represents the projection of the source border area, which is
  59. // problematic for color bleeding. We must check whether any
  60. // destination pixels sample the border area.
  61. outerTransformedRect.inset(kColorBleedTolerance, kColorBleedTolerance);
  62. innerTransformedRect.outset(kColorBleedTolerance, kColorBleedTolerance);
  63. SkIRect outer, inner;
  64. outerTransformedRect.round(&outer);
  65. innerTransformedRect.round(&inner);
  66. // If the inner and outer rects round to the same result, it means the
  67. // border does not overlap any pixel centers. Yay!
  68. return inner != outer;
  69. }
  70. static bool can_ignore_bilerp_constraint(const GrTextureProducer& producer,
  71. const SkRect& srcRect,
  72. const SkMatrix& srcRectToDeviceSpace,
  73. int numSamples) {
  74. if (srcRectToDeviceSpace.rectStaysRect()) {
  75. // sampling is axis-aligned
  76. SkRect transformedRect;
  77. srcRectToDeviceSpace.mapRect(&transformedRect, srcRect);
  78. if (has_aligned_samples(srcRect, transformedRect) ||
  79. !may_color_bleed(srcRect, transformedRect, srcRectToDeviceSpace, numSamples)) {
  80. return true;
  81. }
  82. }
  83. return false;
  84. }
  85. enum class ImageDrawMode {
  86. // Src and dst have been restricted to the image content. May need to clamp, no need to decal.
  87. kOptimized,
  88. // Src and dst are their original sizes, requires use of a decal instead of plain clamping.
  89. // This is used when a dst clip is provided and extends outside of the optimized dst rect.
  90. kDecal,
  91. // Src or dst are empty, or do not intersect the image content so don't draw anything.
  92. kSkip
  93. };
  94. /**
  95. * Optimize the src rect sampling area within an image (sized 'width' x 'height') such that
  96. * 'outSrcRect' will be completely contained in the image's bounds. The corresponding rect
  97. * to draw will be output to 'outDstRect'. The mapping between src and dst will be cached in
  98. * 'srcToDst'. Outputs are not always updated when kSkip is returned.
  99. *
  100. * If 'origSrcRect' is null, implicitly use the image bounds. If 'origDstRect' is null, use the
  101. * original src rect. 'dstClip' should be null when there is no additional clipping.
  102. */
  103. static ImageDrawMode optimize_sample_area(const SkISize& image, const SkRect* origSrcRect,
  104. const SkRect* origDstRect, const SkPoint dstClip[4],
  105. SkRect* outSrcRect, SkRect* outDstRect,
  106. SkMatrix* srcToDst) {
  107. SkRect srcBounds = SkRect::MakeIWH(image.fWidth, image.fHeight);
  108. SkRect src = origSrcRect ? *origSrcRect : srcBounds;
  109. SkRect dst = origDstRect ? *origDstRect : src;
  110. if (src.isEmpty() || dst.isEmpty()) {
  111. return ImageDrawMode::kSkip;
  112. }
  113. if (outDstRect) {
  114. srcToDst->setRectToRect(src, dst, SkMatrix::kFill_ScaleToFit);
  115. } else {
  116. srcToDst->setIdentity();
  117. }
  118. if (origSrcRect && !srcBounds.contains(src)) {
  119. if (!src.intersect(srcBounds)) {
  120. return ImageDrawMode::kSkip;
  121. }
  122. srcToDst->mapRect(&dst, src);
  123. // Both src and dst have gotten smaller. If dstClip is provided, confirm it is still
  124. // contained in dst, otherwise cannot optimize the sample area and must use a decal instead
  125. if (dstClip) {
  126. for (int i = 0; i < 4; ++i) {
  127. if (!dst.contains(dstClip[i].fX, dstClip[i].fY)) {
  128. // Must resort to using a decal mode restricted to the clipped 'src', and
  129. // use the original dst rect (filling in src bounds as needed)
  130. *outSrcRect = src;
  131. *outDstRect = (origDstRect ? *origDstRect
  132. : (origSrcRect ? *origSrcRect : srcBounds));
  133. return ImageDrawMode::kDecal;
  134. }
  135. }
  136. }
  137. }
  138. // The original src and dst were fully contained in the image, or there was no dst clip to
  139. // worry about, or the clip was still contained in the restricted dst rect.
  140. *outSrcRect = src;
  141. *outDstRect = dst;
  142. return ImageDrawMode::kOptimized;
  143. }
  144. /**
  145. * Checks whether the paint is compatible with using GrRenderTargetContext::drawTexture. It is more
  146. * efficient than the GrTextureProducer general case.
  147. */
  148. static bool can_use_draw_texture(const SkPaint& paint) {
  149. return (!paint.getColorFilter() && !paint.getShader() && !paint.getMaskFilter() &&
  150. !paint.getImageFilter() && paint.getFilterQuality() < kMedium_SkFilterQuality);
  151. }
  152. // Assumes srcRect and dstRect have already been optimized to fit the proxy
  153. static void draw_texture(GrRenderTargetContext* rtc, const GrClip& clip, const SkMatrix& ctm,
  154. const SkPaint& paint, const SkRect& srcRect, const SkRect& dstRect,
  155. const SkPoint dstClip[4], GrAA aa, GrQuadAAFlags aaFlags,
  156. SkCanvas::SrcRectConstraint constraint, sk_sp<GrTextureProxy> proxy,
  157. SkAlphaType alphaType, SkColorSpace* colorSpace) {
  158. const GrColorSpaceInfo& dstInfo(rtc->colorSpaceInfo());
  159. auto textureXform =
  160. GrColorSpaceXform::Make(colorSpace , alphaType,
  161. dstInfo.colorSpace(), kPremul_SkAlphaType);
  162. GrSamplerState::Filter filter;
  163. switch (paint.getFilterQuality()) {
  164. case kNone_SkFilterQuality:
  165. filter = GrSamplerState::Filter::kNearest;
  166. break;
  167. case kLow_SkFilterQuality:
  168. filter = GrSamplerState::Filter::kBilerp;
  169. break;
  170. case kMedium_SkFilterQuality:
  171. case kHigh_SkFilterQuality:
  172. SK_ABORT("Quality level not allowed.");
  173. }
  174. // Must specify the strict constraint when the proxy is not functionally exact and the src
  175. // rect would access pixels outside the proxy's content area without the constraint.
  176. if (constraint != SkCanvas::kStrict_SrcRectConstraint &&
  177. !GrProxyProvider::IsFunctionallyExact(proxy.get())) {
  178. // Conservative estimate of how much a coord could be outset from src rect:
  179. // 1/2 pixel for AA and 1/2 pixel for bilerp
  180. float buffer = 0.5f * (aa == GrAA::kYes) +
  181. 0.5f * (filter == GrSamplerState::Filter::kBilerp);
  182. SkRect safeBounds = SkRect::MakeWH(proxy->width(), proxy->height());
  183. safeBounds.inset(buffer, buffer);
  184. if (!safeBounds.contains(srcRect)) {
  185. constraint = SkCanvas::kStrict_SrcRectConstraint;
  186. }
  187. }
  188. SkPMColor4f color;
  189. if (GrPixelConfigIsAlphaOnly(proxy->config())) {
  190. color = SkColor4fPrepForDst(paint.getColor4f(), dstInfo).premul();
  191. } else {
  192. float paintAlpha = paint.getColor4f().fA;
  193. color = { paintAlpha, paintAlpha, paintAlpha, paintAlpha };
  194. }
  195. if (dstClip) {
  196. // Get source coords corresponding to dstClip
  197. SkPoint srcQuad[4];
  198. GrMapRectPoints(dstRect, srcRect, dstClip, srcQuad, 4);
  199. rtc->drawTextureQuad(clip, std::move(proxy), filter, paint.getBlendMode(), color,
  200. srcQuad, dstClip, aa, aaFlags,
  201. constraint == SkCanvas::kStrict_SrcRectConstraint ? &srcRect : nullptr,
  202. ctm, std::move(textureXform));
  203. } else {
  204. rtc->drawTexture(clip, std::move(proxy), filter, paint.getBlendMode(), color, srcRect,
  205. dstRect, aa, aaFlags, constraint, ctm, std::move(textureXform));
  206. }
  207. }
  208. // Assumes srcRect and dstRect have already been optimized to fit the proxy.
  209. static void draw_texture_producer(GrContext* context, GrRenderTargetContext* rtc,
  210. const GrClip& clip, const SkMatrix& ctm,
  211. const SkPaint& paint, GrTextureProducer* producer,
  212. const SkRect& src, const SkRect& dst, const SkPoint dstClip[4],
  213. const SkMatrix& srcToDst, GrAA aa, GrQuadAAFlags aaFlags,
  214. SkCanvas::SrcRectConstraint constraint, bool attemptDrawTexture) {
  215. if (attemptDrawTexture && can_use_draw_texture(paint)) {
  216. // We've done enough checks above to allow us to pass ClampNearest() and not check for
  217. // scaling adjustments.
  218. auto proxy = producer->refTextureProxyForParams(GrSamplerState::ClampNearest(), nullptr);
  219. if (!proxy) {
  220. return;
  221. }
  222. draw_texture(rtc, clip, ctm, paint, src, dst, dstClip, aa, aaFlags, constraint,
  223. std::move(proxy), producer->alphaType(), producer->colorSpace());
  224. return;
  225. }
  226. const SkMaskFilter* mf = paint.getMaskFilter();
  227. // The shader expects proper local coords, so we can't replace local coords with texture coords
  228. // if the shader will be used. If we have a mask filter we will change the underlying geometry
  229. // that is rendered.
  230. bool canUseTextureCoordsAsLocalCoords = !use_shader(producer->isAlphaOnly(), paint) && !mf;
  231. // Specifying the texture coords as local coordinates is an attempt to enable more GrDrawOp
  232. // combining by not baking anything about the srcRect, dstRect, or ctm, into the texture
  233. // FP. In the future this should be an opaque optimization enabled by the combination of
  234. // GrDrawOp/GP and FP.
  235. if (mf && as_MFB(mf)->hasFragmentProcessor()) {
  236. mf = nullptr;
  237. }
  238. bool doBicubic;
  239. GrSamplerState::Filter fm = GrSkFilterQualityToGrFilterMode(
  240. paint.getFilterQuality(), ctm, srcToDst,
  241. context->priv().options().fSharpenMipmappedTextures, &doBicubic);
  242. const GrSamplerState::Filter* filterMode = doBicubic ? nullptr : &fm;
  243. GrTextureProducer::FilterConstraint constraintMode;
  244. if (SkCanvas::kFast_SrcRectConstraint == constraint) {
  245. constraintMode = GrTextureAdjuster::kNo_FilterConstraint;
  246. } else {
  247. constraintMode = GrTextureAdjuster::kYes_FilterConstraint;
  248. }
  249. // If we have to outset for AA then we will generate texture coords outside the src rect. The
  250. // same happens for any mask filter that extends the bounds rendered in the dst.
  251. // This is conservative as a mask filter does not have to expand the bounds rendered.
  252. bool coordsAllInsideSrcRect = aaFlags == GrQuadAAFlags::kNone && !mf;
  253. // Check for optimization to drop the src rect constraint when on bilerp.
  254. if (filterMode && GrSamplerState::Filter::kBilerp == *filterMode &&
  255. GrTextureAdjuster::kYes_FilterConstraint == constraintMode && coordsAllInsideSrcRect &&
  256. !producer->hasMixedResolutions()) {
  257. SkMatrix combinedMatrix;
  258. combinedMatrix.setConcat(ctm, srcToDst);
  259. if (can_ignore_bilerp_constraint(*producer, src, combinedMatrix, rtc->numSamples())) {
  260. constraintMode = GrTextureAdjuster::kNo_FilterConstraint;
  261. }
  262. }
  263. SkMatrix textureMatrix;
  264. if (canUseTextureCoordsAsLocalCoords) {
  265. textureMatrix = SkMatrix::I();
  266. } else {
  267. if (!srcToDst.invert(&textureMatrix)) {
  268. return;
  269. }
  270. }
  271. auto fp = producer->createFragmentProcessor(textureMatrix, src, constraintMode,
  272. coordsAllInsideSrcRect, filterMode);
  273. fp = GrColorSpaceXformEffect::Make(std::move(fp), producer->colorSpace(), producer->alphaType(),
  274. rtc->colorSpaceInfo().colorSpace());
  275. if (!fp) {
  276. return;
  277. }
  278. GrPaint grPaint;
  279. if (!SkPaintToGrPaintWithTexture(context, rtc->colorSpaceInfo(), paint, ctm,
  280. std::move(fp), producer->isAlphaOnly(), &grPaint)) {
  281. return;
  282. }
  283. if (!mf) {
  284. // Can draw the image directly (any mask filter on the paint was converted to an FP already)
  285. if (dstClip) {
  286. SkPoint srcClipPoints[4];
  287. SkPoint* srcClip = nullptr;
  288. if (canUseTextureCoordsAsLocalCoords) {
  289. // Calculate texture coordinates that match the dst clip
  290. GrMapRectPoints(dst, src, dstClip, srcClipPoints, 4);
  291. srcClip = srcClipPoints;
  292. }
  293. rtc->fillQuadWithEdgeAA(clip, std::move(grPaint), aa, aaFlags, ctm, dstClip, srcClip);
  294. } else {
  295. // Provide explicit texture coords when possible, otherwise rely on texture matrix
  296. rtc->fillRectWithEdgeAA(clip, std::move(grPaint), aa, aaFlags, ctm, dst,
  297. canUseTextureCoordsAsLocalCoords ? &src : nullptr);
  298. }
  299. } else {
  300. // Must draw the mask filter as a GrShape. For now, this loses the per-edge AA information
  301. // since it always draws with AA, but that is should not be noticeable since the mask filter
  302. // is probably a blur.
  303. GrShape shape;
  304. if (dstClip) {
  305. // Represent it as an SkPath formed from the dstClip
  306. SkPath path;
  307. path.addPoly(dstClip, 4, true);
  308. shape = GrShape(path);
  309. } else {
  310. shape = GrShape(dst);
  311. }
  312. GrBlurUtils::drawShapeWithMaskFilter(
  313. context, rtc, clip, shape, std::move(grPaint), ctm, mf);
  314. }
  315. }
  316. } // anonymous namespace
  317. //////////////////////////////////////////////////////////////////////////////
  318. void SkGpuDevice::drawImageQuad(const SkImage* image, const SkRect* srcRect, const SkRect* dstRect,
  319. const SkPoint dstClip[4], GrAA aa, GrQuadAAFlags aaFlags,
  320. const SkMatrix* preViewMatrix, const SkPaint& paint,
  321. SkCanvas::SrcRectConstraint constraint) {
  322. SkRect src;
  323. SkRect dst;
  324. SkMatrix srcToDst;
  325. ImageDrawMode mode = optimize_sample_area(SkISize::Make(image->width(), image->height()),
  326. srcRect, dstRect, dstClip, &src, &dst, &srcToDst);
  327. if (mode == ImageDrawMode::kSkip) {
  328. return;
  329. }
  330. if (src.contains(image->bounds())) {
  331. constraint = SkCanvas::kFast_SrcRectConstraint;
  332. }
  333. // Depending on the nature of image, it can flow through more or less optimal pipelines
  334. bool useDecal = mode == ImageDrawMode::kDecal;
  335. bool attemptDrawTexture = !useDecal; // rtc->drawTexture() only clamps
  336. // Get final CTM matrix
  337. SkMatrix ctm = this->ctm();
  338. if (preViewMatrix) {
  339. ctm.preConcat(*preViewMatrix);
  340. }
  341. // YUVA images can be stored in multiple images with different plane resolutions, so this
  342. // uses an effect to combine them dynamically on the GPU. This is done before requesting a
  343. // pinned texture proxy because YUV images force-flatten to RGBA in that scenario.
  344. if (as_IB(image)->isYUVA()) {
  345. SK_HISTOGRAM_BOOLEAN("DrawTiled", false);
  346. LogDrawScaleFactor(ctm, srcToDst, paint.getFilterQuality());
  347. GrYUVAImageTextureMaker maker(fContext.get(), image, useDecal);
  348. draw_texture_producer(fContext.get(), fRenderTargetContext.get(), this->clip(), ctm,
  349. paint, &maker, src, dst, dstClip, srcToDst, aa, aaFlags, constraint,
  350. /* attempt draw texture */ false);
  351. return;
  352. }
  353. // Pinned texture proxies can be rendered directly as textures, or with relatively simple
  354. // adjustments applied to the image content (scaling, mipmaps, color space, etc.)
  355. uint32_t pinnedUniqueID;
  356. if (sk_sp<GrTextureProxy> proxy = as_IB(image)->refPinnedTextureProxy(this->context(),
  357. &pinnedUniqueID)) {
  358. SK_HISTOGRAM_BOOLEAN("DrawTiled", false);
  359. LogDrawScaleFactor(ctm, srcToDst, paint.getFilterQuality());
  360. SkAlphaType alphaType = image->alphaType();
  361. SkColorSpace* colorSpace = as_IB(image)->colorSpace();
  362. if (attemptDrawTexture && can_use_draw_texture(paint)) {
  363. draw_texture(fRenderTargetContext.get(), this->clip(), ctm, paint, src, dst,
  364. dstClip, aa, aaFlags, constraint, std::move(proxy), alphaType, colorSpace);
  365. return;
  366. }
  367. auto colorType = SkColorTypeToGrColorType(image->colorType());
  368. GrTextureAdjuster adjuster(fContext.get(), std::move(proxy), colorType, alphaType,
  369. pinnedUniqueID, colorSpace, useDecal);
  370. draw_texture_producer(fContext.get(), fRenderTargetContext.get(), this->clip(), ctm,
  371. paint, &adjuster, src, dst, dstClip, srcToDst, aa, aaFlags,
  372. constraint, /* attempt draw_texture */ false);
  373. return;
  374. }
  375. // Next up, try tiling the image
  376. // TODO (michaelludwig): Implement this with per-edge AA flags to handle seaming properly
  377. // instead of going through drawBitmapRect (which will be removed from SkDevice in the future)
  378. SkBitmap bm;
  379. if (this->shouldTileImage(image, &src, constraint, paint.getFilterQuality(), ctm, srcToDst)) {
  380. // only support tiling as bitmap at the moment, so force raster-version
  381. if (!as_IB(image)->getROPixels(&bm)) {
  382. return;
  383. }
  384. this->drawBitmapRect(bm, &src, dst, paint, constraint);
  385. return;
  386. }
  387. // This is the funnel for all non-tiled bitmap/image draw calls. Log a histogram entry.
  388. SK_HISTOGRAM_BOOLEAN("DrawTiled", false);
  389. LogDrawScaleFactor(ctm, srcToDst, paint.getFilterQuality());
  390. // Lazily generated images must get drawn as a texture producer that handles the final
  391. // texture creation.
  392. if (image->isLazyGenerated()) {
  393. GrImageTextureMaker maker(fContext.get(), image, SkImage::kAllow_CachingHint, useDecal);
  394. draw_texture_producer(fContext.get(), fRenderTargetContext.get(), this->clip(), ctm,
  395. paint, &maker, src, dst, dstClip, srcToDst, aa, aaFlags, constraint,
  396. attemptDrawTexture);
  397. return;
  398. }
  399. if (as_IB(image)->getROPixels(&bm)) {
  400. GrBitmapTextureMaker maker(fContext.get(), bm, useDecal);
  401. draw_texture_producer(fContext.get(), fRenderTargetContext.get(), this->clip(), ctm,
  402. paint, &maker, src, dst, dstClip, srcToDst, aa, aaFlags, constraint,
  403. attemptDrawTexture);
  404. }
  405. // Otherwise don't know how to draw it
  406. }
  407. void SkGpuDevice::drawEdgeAAImageSet(const SkCanvas::ImageSetEntry set[], int count,
  408. const SkPoint dstClips[], const SkMatrix preViewMatrices[],
  409. const SkPaint& paint, SkCanvas::SrcRectConstraint constraint) {
  410. SkASSERT(count > 0);
  411. if (!can_use_draw_texture(paint)) {
  412. // Send every entry through drawImageQuad() to handle the more complicated paint
  413. int dstClipIndex = 0;
  414. for (int i = 0; i < count; ++i) {
  415. // Only no clip or quad clip are supported
  416. SkASSERT(!set[i].fHasClip || dstClips);
  417. SkASSERT(set[i].fMatrixIndex < 0 || preViewMatrices);
  418. // Always send GrAA::kYes to preserve seaming across tiling in MSAA
  419. this->drawImageQuad(set[i].fImage.get(), &set[i].fSrcRect, &set[i].fDstRect,
  420. set[i].fHasClip ? dstClips + dstClipIndex : nullptr,
  421. GrAA::kYes, SkToGrQuadAAFlags(set[i].fAAFlags),
  422. set[i].fMatrixIndex < 0 ? nullptr : preViewMatrices + set[i].fMatrixIndex,
  423. paint, constraint);
  424. dstClipIndex += 4 * set[i].fHasClip;
  425. }
  426. return;
  427. }
  428. GrSamplerState::Filter filter = kNone_SkFilterQuality == paint.getFilterQuality() ?
  429. GrSamplerState::Filter::kNearest : GrSamplerState::Filter::kBilerp;
  430. SkBlendMode mode = paint.getBlendMode();
  431. SkAutoTArray<GrRenderTargetContext::TextureSetEntry> textures(count);
  432. // We accumulate compatible proxies until we find an an incompatible one or reach the end and
  433. // issue the accumulated 'n' draws starting at 'base'.
  434. int base = 0, n = 0;
  435. auto draw = [&] {
  436. if (n > 0) {
  437. auto textureXform = GrColorSpaceXform::Make(
  438. set[base].fImage->colorSpace(), set[base].fImage->alphaType(),
  439. fRenderTargetContext->colorSpaceInfo().colorSpace(), kPremul_SkAlphaType);
  440. fRenderTargetContext->drawTextureSet(this->clip(), textures.get() + base, n,
  441. filter, mode, GrAA::kYes, constraint, this->ctm(),
  442. std::move(textureXform));
  443. }
  444. };
  445. int dstClipIndex = 0;
  446. for (int i = 0; i < count; ++i) {
  447. SkASSERT(!set[i].fHasClip || dstClips);
  448. SkASSERT(set[i].fMatrixIndex < 0 || preViewMatrices);
  449. // Manage the dst clip pointer tracking before any continues are used so we don't lose
  450. // our place in the dstClips array.
  451. const SkPoint* clip = set[i].fHasClip ? dstClips + dstClipIndex : nullptr;
  452. dstClipIndex += 4 * set[i].fHasClip;
  453. // The default SkBaseDevice implementation is based on drawImageRect which does not allow
  454. // non-sorted src rects. TODO: Decide this is OK or make sure we handle it.
  455. if (!set[i].fSrcRect.isSorted()) {
  456. draw();
  457. base = i + 1;
  458. n = 0;
  459. continue;
  460. }
  461. sk_sp<GrTextureProxy> proxy;
  462. const SkImage_Base* image = as_IB(set[i].fImage.get());
  463. // Extract proxy from image, but skip YUV images so they get processed through
  464. // drawImageQuad and the proper effect to dynamically sample their planes.
  465. if (!image->isYUVA()) {
  466. uint32_t uniqueID;
  467. proxy = image->refPinnedTextureProxy(this->context(), &uniqueID);
  468. if (!proxy) {
  469. proxy = image->asTextureProxyRef(this->context(), GrSamplerState::ClampBilerp(),
  470. nullptr);
  471. }
  472. }
  473. if (!proxy) {
  474. // This image can't go through the texture op, send through general image pipeline
  475. // after flushing current batch.
  476. draw();
  477. base = i + 1;
  478. n = 0;
  479. this->drawImageQuad(image, &set[i].fSrcRect, &set[i].fDstRect, clip, GrAA::kYes,
  480. SkToGrQuadAAFlags(set[i].fAAFlags),
  481. set[i].fMatrixIndex < 0 ? nullptr : preViewMatrices + set[i].fMatrixIndex,
  482. paint, constraint);
  483. continue;
  484. }
  485. textures[i].fProxy = std::move(proxy);
  486. textures[i].fSrcRect = set[i].fSrcRect;
  487. textures[i].fDstRect = set[i].fDstRect;
  488. textures[i].fDstClipQuad = clip;
  489. textures[i].fPreViewMatrix =
  490. set[i].fMatrixIndex < 0 ? nullptr : preViewMatrices + set[i].fMatrixIndex;
  491. textures[i].fAlpha = set[i].fAlpha * paint.getAlphaf();
  492. textures[i].fAAFlags = SkToGrQuadAAFlags(set[i].fAAFlags);
  493. if (n > 0 &&
  494. (!GrTextureProxy::ProxiesAreCompatibleAsDynamicState(textures[i].fProxy.get(),
  495. textures[base].fProxy.get()) ||
  496. set[i].fImage->alphaType() != set[base].fImage->alphaType() ||
  497. !SkColorSpace::Equals(set[i].fImage->colorSpace(), set[base].fImage->colorSpace()))) {
  498. draw();
  499. base = i;
  500. n = 1;
  501. } else {
  502. ++n;
  503. }
  504. }
  505. draw();
  506. }
  507. // TODO (michaelludwig) - to be removed when drawBitmapRect doesn't need it anymore
  508. void SkGpuDevice::drawTextureProducer(GrTextureProducer* producer,
  509. const SkRect* srcRect,
  510. const SkRect* dstRect,
  511. SkCanvas::SrcRectConstraint constraint,
  512. const SkMatrix& viewMatrix,
  513. const SkPaint& paint,
  514. bool attemptDrawTexture) {
  515. // The texture refactor split the old logic of drawTextureProducer into the beginning of
  516. // drawImageQuad() and into the static draw_texture_producer. Replicate necessary logic that
  517. // drawImageQuad() handles.
  518. SkRect src;
  519. SkRect dst;
  520. SkMatrix srcToDst;
  521. ImageDrawMode mode = optimize_sample_area(SkISize::Make(producer->width(), producer->height()),
  522. srcRect, dstRect, nullptr, &src, &dst, &srcToDst);
  523. if (mode == ImageDrawMode::kSkip) {
  524. return;
  525. }
  526. // There's no dstClip to worry about and the producer is already made so we wouldn't be able
  527. // to tell it to use decals if we had to
  528. SkASSERT(mode != ImageDrawMode::kDecal);
  529. draw_texture_producer(fContext.get(), fRenderTargetContext.get(), this->clip(), viewMatrix,
  530. paint, producer, src, dst, /* clip */ nullptr, srcToDst,
  531. GrAA(paint.isAntiAlias()),
  532. paint.isAntiAlias() ? GrQuadAAFlags::kAll : GrQuadAAFlags::kNone,
  533. constraint, attemptDrawTexture);
  534. }