SkMatrixConvolutionImageFilter.cpp 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471
  1. /*
  2. * Copyright 2012 The Android Open Source Project
  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 "include/core/SkBitmap.h"
  8. #include "include/core/SkRect.h"
  9. #include "include/core/SkUnPreMultiply.h"
  10. #include "include/effects/SkMatrixConvolutionImageFilter.h"
  11. #include "include/private/SkColorData.h"
  12. #include "src/core/SkImageFilterPriv.h"
  13. #include "src/core/SkReadBuffer.h"
  14. #include "src/core/SkSpecialImage.h"
  15. #include "src/core/SkWriteBuffer.h"
  16. #if SK_SUPPORT_GPU
  17. #include "include/gpu/GrContext.h"
  18. #include "src/gpu/GrTextureProxy.h"
  19. #include "src/gpu/effects/GrMatrixConvolutionEffect.h"
  20. #endif
  21. // We need to be able to read at most SK_MaxS32 bytes, so divide that
  22. // by the size of a scalar to know how many scalars we can read.
  23. static const int32_t gMaxKernelSize = SK_MaxS32 / sizeof(SkScalar);
  24. SkMatrixConvolutionImageFilter::SkMatrixConvolutionImageFilter(const SkISize& kernelSize,
  25. const SkScalar* kernel,
  26. SkScalar gain,
  27. SkScalar bias,
  28. const SkIPoint& kernelOffset,
  29. TileMode tileMode,
  30. bool convolveAlpha,
  31. sk_sp<SkImageFilter> input,
  32. const CropRect* cropRect)
  33. : INHERITED(&input, 1, cropRect)
  34. , fKernelSize(kernelSize)
  35. , fGain(gain)
  36. , fBias(bias)
  37. , fKernelOffset(kernelOffset)
  38. , fTileMode(tileMode)
  39. , fConvolveAlpha(convolveAlpha) {
  40. size_t size = (size_t) sk_64_mul(fKernelSize.width(), fKernelSize.height());
  41. fKernel = new SkScalar[size];
  42. memcpy(fKernel, kernel, size * sizeof(SkScalar));
  43. SkASSERT(kernelSize.fWidth >= 1 && kernelSize.fHeight >= 1);
  44. SkASSERT(kernelOffset.fX >= 0 && kernelOffset.fX < kernelSize.fWidth);
  45. SkASSERT(kernelOffset.fY >= 0 && kernelOffset.fY < kernelSize.fHeight);
  46. }
  47. sk_sp<SkImageFilter> SkMatrixConvolutionImageFilter::Make(const SkISize& kernelSize,
  48. const SkScalar* kernel,
  49. SkScalar gain,
  50. SkScalar bias,
  51. const SkIPoint& kernelOffset,
  52. TileMode tileMode,
  53. bool convolveAlpha,
  54. sk_sp<SkImageFilter> input,
  55. const CropRect* cropRect) {
  56. if (kernelSize.width() < 1 || kernelSize.height() < 1) {
  57. return nullptr;
  58. }
  59. if (gMaxKernelSize / kernelSize.fWidth < kernelSize.fHeight) {
  60. return nullptr;
  61. }
  62. if (!kernel) {
  63. return nullptr;
  64. }
  65. if ((kernelOffset.fX < 0) || (kernelOffset.fX >= kernelSize.fWidth) ||
  66. (kernelOffset.fY < 0) || (kernelOffset.fY >= kernelSize.fHeight)) {
  67. return nullptr;
  68. }
  69. return sk_sp<SkImageFilter>(new SkMatrixConvolutionImageFilter(kernelSize, kernel, gain,
  70. bias, kernelOffset,
  71. tileMode, convolveAlpha,
  72. std::move(input), cropRect));
  73. }
  74. sk_sp<SkFlattenable> SkMatrixConvolutionImageFilter::CreateProc(SkReadBuffer& buffer) {
  75. SK_IMAGEFILTER_UNFLATTEN_COMMON(common, 1);
  76. SkISize kernelSize;
  77. kernelSize.fWidth = buffer.readInt();
  78. kernelSize.fHeight = buffer.readInt();
  79. const int count = buffer.getArrayCount();
  80. const int64_t kernelArea = sk_64_mul(kernelSize.width(), kernelSize.height());
  81. if (!buffer.validate(kernelArea == count)) {
  82. return nullptr;
  83. }
  84. if (!buffer.validateCanReadN<SkScalar>(count)) {
  85. return nullptr;
  86. }
  87. SkAutoSTArray<16, SkScalar> kernel(count);
  88. if (!buffer.readScalarArray(kernel.get(), count)) {
  89. return nullptr;
  90. }
  91. SkScalar gain = buffer.readScalar();
  92. SkScalar bias = buffer.readScalar();
  93. SkIPoint kernelOffset;
  94. kernelOffset.fX = buffer.readInt();
  95. kernelOffset.fY = buffer.readInt();
  96. TileMode tileMode = buffer.read32LE(kLast_TileMode);
  97. bool convolveAlpha = buffer.readBool();
  98. if (!buffer.isValid()) {
  99. return nullptr;
  100. }
  101. return Make(kernelSize, kernel.get(), gain, bias, kernelOffset, tileMode,
  102. convolveAlpha, common.getInput(0), &common.cropRect());
  103. }
  104. void SkMatrixConvolutionImageFilter::flatten(SkWriteBuffer& buffer) const {
  105. this->INHERITED::flatten(buffer);
  106. buffer.writeInt(fKernelSize.fWidth);
  107. buffer.writeInt(fKernelSize.fHeight);
  108. buffer.writeScalarArray(fKernel, fKernelSize.fWidth * fKernelSize.fHeight);
  109. buffer.writeScalar(fGain);
  110. buffer.writeScalar(fBias);
  111. buffer.writeInt(fKernelOffset.fX);
  112. buffer.writeInt(fKernelOffset.fY);
  113. buffer.writeInt((int) fTileMode);
  114. buffer.writeBool(fConvolveAlpha);
  115. }
  116. SkMatrixConvolutionImageFilter::~SkMatrixConvolutionImageFilter() {
  117. delete[] fKernel;
  118. }
  119. class UncheckedPixelFetcher {
  120. public:
  121. static inline SkPMColor fetch(const SkBitmap& src, int x, int y, const SkIRect& bounds) {
  122. return *src.getAddr32(x, y);
  123. }
  124. };
  125. class ClampPixelFetcher {
  126. public:
  127. static inline SkPMColor fetch(const SkBitmap& src, int x, int y, const SkIRect& bounds) {
  128. x = SkTPin(x, bounds.fLeft, bounds.fRight - 1);
  129. y = SkTPin(y, bounds.fTop, bounds.fBottom - 1);
  130. return *src.getAddr32(x, y);
  131. }
  132. };
  133. class RepeatPixelFetcher {
  134. public:
  135. static inline SkPMColor fetch(const SkBitmap& src, int x, int y, const SkIRect& bounds) {
  136. x = (x - bounds.left()) % bounds.width() + bounds.left();
  137. y = (y - bounds.top()) % bounds.height() + bounds.top();
  138. if (x < bounds.left()) {
  139. x += bounds.width();
  140. }
  141. if (y < bounds.top()) {
  142. y += bounds.height();
  143. }
  144. return *src.getAddr32(x, y);
  145. }
  146. };
  147. class ClampToBlackPixelFetcher {
  148. public:
  149. static inline SkPMColor fetch(const SkBitmap& src, int x, int y, const SkIRect& bounds) {
  150. if (x < bounds.fLeft || x >= bounds.fRight || y < bounds.fTop || y >= bounds.fBottom) {
  151. return 0;
  152. } else {
  153. return *src.getAddr32(x, y);
  154. }
  155. }
  156. };
  157. template<class PixelFetcher, bool convolveAlpha>
  158. void SkMatrixConvolutionImageFilter::filterPixels(const SkBitmap& src,
  159. SkBitmap* result,
  160. SkIVector& offset,
  161. const SkIRect& r,
  162. const SkIRect& bounds) const {
  163. SkIRect rect(r);
  164. if (!rect.intersect(bounds)) {
  165. return;
  166. }
  167. for (int y = rect.fTop; y < rect.fBottom; ++y) {
  168. SkPMColor* dptr = result->getAddr32(rect.fLeft - offset.fX, y - offset.fY);
  169. for (int x = rect.fLeft; x < rect.fRight; ++x) {
  170. SkScalar sumA = 0, sumR = 0, sumG = 0, sumB = 0;
  171. for (int cy = 0; cy < fKernelSize.fHeight; cy++) {
  172. for (int cx = 0; cx < fKernelSize.fWidth; cx++) {
  173. SkPMColor s = PixelFetcher::fetch(src,
  174. x + cx - fKernelOffset.fX,
  175. y + cy - fKernelOffset.fY,
  176. bounds);
  177. SkScalar k = fKernel[cy * fKernelSize.fWidth + cx];
  178. if (convolveAlpha) {
  179. sumA += SkGetPackedA32(s) * k;
  180. }
  181. sumR += SkGetPackedR32(s) * k;
  182. sumG += SkGetPackedG32(s) * k;
  183. sumB += SkGetPackedB32(s) * k;
  184. }
  185. }
  186. int a = convolveAlpha
  187. ? SkClampMax(SkScalarFloorToInt(sumA * fGain + fBias), 255)
  188. : 255;
  189. int r = SkClampMax(SkScalarFloorToInt(sumR * fGain + fBias), a);
  190. int g = SkClampMax(SkScalarFloorToInt(sumG * fGain + fBias), a);
  191. int b = SkClampMax(SkScalarFloorToInt(sumB * fGain + fBias), a);
  192. if (!convolveAlpha) {
  193. a = SkGetPackedA32(PixelFetcher::fetch(src, x, y, bounds));
  194. *dptr++ = SkPreMultiplyARGB(a, r, g, b);
  195. } else {
  196. *dptr++ = SkPackARGB32(a, r, g, b);
  197. }
  198. }
  199. }
  200. }
  201. template<class PixelFetcher>
  202. void SkMatrixConvolutionImageFilter::filterPixels(const SkBitmap& src,
  203. SkBitmap* result,
  204. SkIVector& offset,
  205. const SkIRect& rect,
  206. const SkIRect& bounds) const {
  207. if (fConvolveAlpha) {
  208. filterPixels<PixelFetcher, true>(src, result, offset, rect, bounds);
  209. } else {
  210. filterPixels<PixelFetcher, false>(src, result, offset, rect, bounds);
  211. }
  212. }
  213. void SkMatrixConvolutionImageFilter::filterInteriorPixels(const SkBitmap& src,
  214. SkBitmap* result,
  215. SkIVector& offset,
  216. const SkIRect& rect,
  217. const SkIRect& bounds) const {
  218. switch (fTileMode) {
  219. case kRepeat_TileMode:
  220. // In repeat mode, we still need to wrap the samples around the src
  221. filterPixels<RepeatPixelFetcher>(src, result, offset, rect, bounds);
  222. break;
  223. case kClamp_TileMode:
  224. case kClampToBlack_TileMode:
  225. filterPixels<UncheckedPixelFetcher>(src, result, offset, rect, bounds);
  226. break;
  227. }
  228. }
  229. void SkMatrixConvolutionImageFilter::filterBorderPixels(const SkBitmap& src,
  230. SkBitmap* result,
  231. SkIVector& offset,
  232. const SkIRect& rect,
  233. const SkIRect& srcBounds) const {
  234. switch (fTileMode) {
  235. case kClamp_TileMode:
  236. filterPixels<ClampPixelFetcher>(src, result, offset, rect, srcBounds);
  237. break;
  238. case kRepeat_TileMode:
  239. filterPixels<RepeatPixelFetcher>(src, result, offset, rect, srcBounds);
  240. break;
  241. case kClampToBlack_TileMode:
  242. filterPixels<ClampToBlackPixelFetcher>(src, result, offset, rect, srcBounds);
  243. break;
  244. }
  245. }
  246. // FIXME: This should be refactored to SkImageFilterUtils for
  247. // use by other filters. For now, we assume the input is always
  248. // premultiplied and unpremultiply it
  249. static SkBitmap unpremultiply_bitmap(const SkBitmap& src) {
  250. if (!src.getPixels()) {
  251. return SkBitmap();
  252. }
  253. const SkImageInfo info = SkImageInfo::MakeN32(src.width(), src.height(), src.alphaType());
  254. SkBitmap result;
  255. if (!result.tryAllocPixels(info)) {
  256. return SkBitmap();
  257. }
  258. for (int y = 0; y < src.height(); ++y) {
  259. const uint32_t* srcRow = src.getAddr32(0, y);
  260. uint32_t* dstRow = result.getAddr32(0, y);
  261. for (int x = 0; x < src.width(); ++x) {
  262. dstRow[x] = SkUnPreMultiply::PMColorToColor(srcRow[x]);
  263. }
  264. }
  265. return result;
  266. }
  267. #if SK_SUPPORT_GPU
  268. static GrTextureDomain::Mode convert_tilemodes(SkMatrixConvolutionImageFilter::TileMode tileMode) {
  269. switch (tileMode) {
  270. case SkMatrixConvolutionImageFilter::kClamp_TileMode:
  271. return GrTextureDomain::kClamp_Mode;
  272. case SkMatrixConvolutionImageFilter::kRepeat_TileMode:
  273. return GrTextureDomain::kRepeat_Mode;
  274. case SkMatrixConvolutionImageFilter::kClampToBlack_TileMode:
  275. return GrTextureDomain::kDecal_Mode;
  276. default:
  277. SkASSERT(false);
  278. }
  279. return GrTextureDomain::kIgnore_Mode;
  280. }
  281. #endif
  282. sk_sp<SkSpecialImage> SkMatrixConvolutionImageFilter::onFilterImage(SkSpecialImage* source,
  283. const Context& ctx,
  284. SkIPoint* offset) const {
  285. SkIPoint inputOffset = SkIPoint::Make(0, 0);
  286. sk_sp<SkSpecialImage> input(this->filterInput(0, source, ctx, &inputOffset));
  287. if (!input) {
  288. return nullptr;
  289. }
  290. SkIRect dstBounds;
  291. input = this->applyCropRectAndPad(this->mapContext(ctx), input.get(), &inputOffset, &dstBounds);
  292. if (!input) {
  293. return nullptr;
  294. }
  295. const SkIRect originalSrcBounds = SkIRect::MakeXYWH(inputOffset.fX, inputOffset.fY,
  296. input->width(), input->height());
  297. SkIRect srcBounds = this->onFilterNodeBounds(dstBounds, ctx.ctm(), kReverse_MapDirection,
  298. &originalSrcBounds);
  299. if (kRepeat_TileMode == fTileMode) {
  300. srcBounds = DetermineRepeatedSrcBound(srcBounds, fKernelOffset,
  301. fKernelSize, originalSrcBounds);
  302. } else {
  303. if (!srcBounds.intersect(dstBounds)) {
  304. return nullptr;
  305. }
  306. }
  307. #if SK_SUPPORT_GPU
  308. // Note: if the kernel is too big, the GPU path falls back to SW
  309. if (source->isTextureBacked() &&
  310. fKernelSize.width() * fKernelSize.height() <= MAX_KERNEL_SIZE) {
  311. auto context = source->getContext();
  312. // Ensure the input is in the destination color space. Typically applyCropRect will have
  313. // called pad_image to account for our dilation of bounds, so the result will already be
  314. // moved to the destination color space. If a filter DAG avoids that, then we use this
  315. // fall-back, which saves us from having to do the xform during the filter itself.
  316. input = ImageToColorSpace(input.get(), ctx.outputProperties());
  317. sk_sp<GrTextureProxy> inputProxy(input->asTextureProxyRef(context));
  318. SkASSERT(inputProxy);
  319. const auto isProtected = inputProxy->isProtected();
  320. offset->fX = dstBounds.left();
  321. offset->fY = dstBounds.top();
  322. dstBounds.offset(-inputOffset);
  323. srcBounds.offset(-inputOffset);
  324. // Map srcBounds from input's logical image domain to that of the proxy
  325. srcBounds.offset(input->subset().x(), input->subset().y());
  326. auto fp = GrMatrixConvolutionEffect::Make(std::move(inputProxy),
  327. srcBounds,
  328. fKernelSize,
  329. fKernel,
  330. fGain,
  331. fBias,
  332. fKernelOffset,
  333. convert_tilemodes(fTileMode),
  334. fConvolveAlpha);
  335. if (!fp) {
  336. return nullptr;
  337. }
  338. return DrawWithFP(context, std::move(fp), dstBounds, ctx.outputProperties(),
  339. isProtected ? GrProtected::kYes : GrProtected::kNo);
  340. }
  341. #endif
  342. SkBitmap inputBM;
  343. if (!input->getROPixels(&inputBM)) {
  344. return nullptr;
  345. }
  346. if (inputBM.colorType() != kN32_SkColorType) {
  347. return nullptr;
  348. }
  349. if (!fConvolveAlpha && !inputBM.isOpaque()) {
  350. inputBM = unpremultiply_bitmap(inputBM);
  351. }
  352. if (!inputBM.getPixels()) {
  353. return nullptr;
  354. }
  355. const SkImageInfo info = SkImageInfo::MakeN32(dstBounds.width(), dstBounds.height(),
  356. inputBM.alphaType());
  357. SkBitmap dst;
  358. if (!dst.tryAllocPixels(info)) {
  359. return nullptr;
  360. }
  361. offset->fX = dstBounds.fLeft;
  362. offset->fY = dstBounds.fTop;
  363. dstBounds.offset(-inputOffset);
  364. srcBounds.offset(-inputOffset);
  365. SkIRect interior;
  366. if (kRepeat_TileMode == fTileMode) {
  367. // In repeat mode, the filterPixels calls will wrap around
  368. // so we just need to render 'dstBounds'
  369. interior = dstBounds;
  370. } else {
  371. interior = SkIRect::MakeXYWH(dstBounds.left() + fKernelOffset.fX,
  372. dstBounds.top() + fKernelOffset.fY,
  373. dstBounds.width() - fKernelSize.fWidth + 1,
  374. dstBounds.height() - fKernelSize.fHeight + 1);
  375. }
  376. SkIRect top = SkIRect::MakeLTRB(dstBounds.left(), dstBounds.top(),
  377. dstBounds.right(), interior.top());
  378. SkIRect bottom = SkIRect::MakeLTRB(dstBounds.left(), interior.bottom(),
  379. dstBounds.right(), dstBounds.bottom());
  380. SkIRect left = SkIRect::MakeLTRB(dstBounds.left(), interior.top(),
  381. interior.left(), interior.bottom());
  382. SkIRect right = SkIRect::MakeLTRB(interior.right(), interior.top(),
  383. dstBounds.right(), interior.bottom());
  384. SkIVector dstContentOffset = { offset->fX - inputOffset.fX, offset->fY - inputOffset.fY };
  385. this->filterBorderPixels(inputBM, &dst, dstContentOffset, top, srcBounds);
  386. this->filterBorderPixels(inputBM, &dst, dstContentOffset, left, srcBounds);
  387. this->filterInteriorPixels(inputBM, &dst, dstContentOffset, interior, srcBounds);
  388. this->filterBorderPixels(inputBM, &dst, dstContentOffset, right, srcBounds);
  389. this->filterBorderPixels(inputBM, &dst, dstContentOffset, bottom, srcBounds);
  390. return SkSpecialImage::MakeFromRaster(SkIRect::MakeWH(dstBounds.width(), dstBounds.height()),
  391. dst);
  392. }
  393. SkIRect SkMatrixConvolutionImageFilter::onFilterNodeBounds(const SkIRect& src, const SkMatrix& ctm,
  394. MapDirection dir,
  395. const SkIRect* inputRect) const {
  396. if (kReverse_MapDirection == dir && kRepeat_TileMode == fTileMode && inputRect) {
  397. SkASSERT(inputRect);
  398. return DetermineRepeatedSrcBound(src, fKernelOffset, fKernelSize, *inputRect);
  399. }
  400. SkIRect dst = src;
  401. int w = fKernelSize.width() - 1, h = fKernelSize.height() - 1;
  402. if (kReverse_MapDirection == dir) {
  403. dst.adjust(-fKernelOffset.fX, -fKernelOffset.fY,
  404. w - fKernelOffset.fX, h - fKernelOffset.fY);
  405. } else {
  406. dst.adjust(fKernelOffset.fX - w, fKernelOffset.fY - h, fKernelOffset.fX, fKernelOffset.fY);
  407. }
  408. return dst;
  409. }
  410. bool SkMatrixConvolutionImageFilter::affectsTransparentBlack() const {
  411. // It seems that the only rational way for repeat sample mode to work is if the caller
  412. // explicitly restricts the input in which case the input range is explicitly known and
  413. // specified.
  414. // TODO: is seems that this should be true for clamp mode too.
  415. // For the other modes, because the kernel is applied in device-space, we have no idea what
  416. // pixels it will affect in object-space.
  417. return kRepeat_TileMode != fTileMode;
  418. }