blob: b0fc289eb847c59b935f907b790788823d7014f8 [file] [log] [blame]
senorblanco@chromium.org05054f12012-03-02 21:05:45 +00001/*
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
8#include "SkMorphologyImageFilter.h"
djsollen@google.com64a0ec32012-06-12 15:17:27 +00009#include "SkBitmap.h"
senorblanco@chromium.org05054f12012-03-02 21:05:45 +000010#include "SkColorPriv.h"
bsalomon@google.comcf8fb1f2012-08-02 14:03:32 +000011#if SK_SUPPORT_GPU
senorblanco@chromium.org302cffb2012-08-01 20:16:34 +000012#include "GrContext.h"
13#include "GrTexture.h"
bsalomon@google.comcf8fb1f2012-08-02 14:03:32 +000014#endif
senorblanco@chromium.org05054f12012-03-02 21:05:45 +000015
16SkMorphologyImageFilter::SkMorphologyImageFilter(SkFlattenableReadBuffer& buffer)
17 : INHERITED(buffer) {
tomhudson@google.com75589252012-04-10 17:42:21 +000018 fRadius.fWidth = buffer.readInt();
19 fRadius.fHeight = buffer.readInt();
senorblanco@chromium.org05054f12012-03-02 21:05:45 +000020}
21
22SkMorphologyImageFilter::SkMorphologyImageFilter(int radiusX, int radiusY)
23 : fRadius(SkISize::Make(radiusX, radiusY)) {
24}
25
26
djsollen@google.com54924242012-03-29 15:18:04 +000027void SkMorphologyImageFilter::flatten(SkFlattenableWriteBuffer& buffer) const {
senorblanco@chromium.org05054f12012-03-02 21:05:45 +000028 this->INHERITED::flatten(buffer);
tomhudson@google.com75589252012-04-10 17:42:21 +000029 buffer.writeInt(fRadius.fWidth);
30 buffer.writeInt(fRadius.fHeight);
senorblanco@chromium.org05054f12012-03-02 21:05:45 +000031}
32
33static void erode(const SkPMColor* src, SkPMColor* dst,
34 int radius, int width, int height,
35 int srcStrideX, int srcStrideY,
36 int dstStrideX, int dstStrideY)
37{
senorblanco@chromium.org56dd6302012-04-10 17:25:44 +000038 radius = SkMin32(radius, width - 1);
39 const SkPMColor* upperSrc = src + radius * srcStrideX;
senorblanco@chromium.org05054f12012-03-02 21:05:45 +000040 for (int x = 0; x < width; ++x) {
41 const SkPMColor* lp = src;
42 const SkPMColor* up = upperSrc;
43 SkPMColor* dptr = dst;
44 for (int y = 0; y < height; ++y) {
45 int minB = 255, minG = 255, minR = 255, minA = 255;
46 for (const SkPMColor* p = lp; p <= up; p += srcStrideX) {
47 int b = SkGetPackedB32(*p);
48 int g = SkGetPackedG32(*p);
49 int r = SkGetPackedR32(*p);
50 int a = SkGetPackedA32(*p);
51 if (b < minB) minB = b;
52 if (g < minG) minG = g;
53 if (r < minR) minR = r;
54 if (a < minA) minA = a;
55 }
56 *dptr = SkPackARGB32(minA, minR, minG, minB);
57 dptr += dstStrideY;
58 lp += srcStrideY;
59 up += srcStrideY;
60 }
61 if (x >= radius) src += srcStrideX;
62 if (x + radius < width - 1) upperSrc += srcStrideX;
63 dst += dstStrideX;
64 }
65}
66
67static void erodeX(const SkBitmap& src, SkBitmap* dst, int radiusX)
68{
69 erode(src.getAddr32(0, 0), dst->getAddr32(0, 0),
70 radiusX, src.width(), src.height(),
71 1, src.rowBytesAsPixels(), 1, dst->rowBytesAsPixels());
72}
73
74static void erodeY(const SkBitmap& src, SkBitmap* dst, int radiusY)
75{
76 erode(src.getAddr32(0, 0), dst->getAddr32(0, 0),
77 radiusY, src.height(), src.width(),
78 src.rowBytesAsPixels(), 1, dst->rowBytesAsPixels(), 1);
79}
80
81static void dilate(const SkPMColor* src, SkPMColor* dst,
82 int radius, int width, int height,
83 int srcStrideX, int srcStrideY,
84 int dstStrideX, int dstStrideY)
85{
senorblanco@chromium.org56dd6302012-04-10 17:25:44 +000086 radius = SkMin32(radius, width - 1);
87 const SkPMColor* upperSrc = src + radius * srcStrideX;
senorblanco@chromium.org05054f12012-03-02 21:05:45 +000088 for (int x = 0; x < width; ++x) {
89 const SkPMColor* lp = src;
90 const SkPMColor* up = upperSrc;
91 SkPMColor* dptr = dst;
92 for (int y = 0; y < height; ++y) {
93 int maxB = 0, maxG = 0, maxR = 0, maxA = 0;
94 for (const SkPMColor* p = lp; p <= up; p += srcStrideX) {
95 int b = SkGetPackedB32(*p);
96 int g = SkGetPackedG32(*p);
97 int r = SkGetPackedR32(*p);
98 int a = SkGetPackedA32(*p);
99 if (b > maxB) maxB = b;
100 if (g > maxG) maxG = g;
101 if (r > maxR) maxR = r;
102 if (a > maxA) maxA = a;
103 }
104 *dptr = SkPackARGB32(maxA, maxR, maxG, maxB);
105 dptr += dstStrideY;
106 lp += srcStrideY;
107 up += srcStrideY;
108 }
109 if (x >= radius) src += srcStrideX;
110 if (x + radius < width - 1) upperSrc += srcStrideX;
111 dst += dstStrideX;
112 }
113}
114
115static void dilateX(const SkBitmap& src, SkBitmap* dst, int radiusX)
116{
117 dilate(src.getAddr32(0, 0), dst->getAddr32(0, 0),
118 radiusX, src.width(), src.height(),
119 1, src.rowBytesAsPixels(), 1, dst->rowBytesAsPixels());
120}
121
122static void dilateY(const SkBitmap& src, SkBitmap* dst, int radiusY)
123{
124 dilate(src.getAddr32(0, 0), dst->getAddr32(0, 0),
125 radiusY, src.height(), src.width(),
126 src.rowBytesAsPixels(), 1, dst->rowBytesAsPixels(), 1);
127}
128
129bool SkErodeImageFilter::onFilterImage(Proxy*,
130 const SkBitmap& src, const SkMatrix&,
131 SkBitmap* dst, SkIPoint*) {
132 if (src.config() != SkBitmap::kARGB_8888_Config) {
133 return false;
134 }
135
136 SkAutoLockPixels alp(src);
137 if (!src.getPixels()) {
138 return false;
139 }
140
141 dst->setConfig(src.config(), src.width(), src.height());
142 dst->allocPixels();
143
144 int width = radius().width();
145 int height = radius().height();
146
147 if (width < 0 || height < 0) {
148 return false;
149 }
150
151 if (width == 0 && height == 0) {
152 src.copyTo(dst, dst->config());
153 return true;
154 }
155
156 SkBitmap temp;
157 temp.setConfig(dst->config(), dst->width(), dst->height());
158 if (!temp.allocPixels()) {
159 return false;
160 }
161
162 if (width > 0 && height > 0) {
163 erodeX(src, &temp, width);
164 erodeY(temp, dst, height);
165 } else if (width > 0) {
166 erodeX(src, dst, width);
167 } else if (height > 0) {
168 erodeY(src, dst, height);
169 }
170 return true;
171}
172
173bool SkDilateImageFilter::onFilterImage(Proxy*,
174 const SkBitmap& src, const SkMatrix&,
175 SkBitmap* dst, SkIPoint*) {
176 if (src.config() != SkBitmap::kARGB_8888_Config) {
177 return false;
178 }
179
180 SkAutoLockPixels alp(src);
181 if (!src.getPixels()) {
182 return false;
183 }
184
185 dst->setConfig(src.config(), src.width(), src.height());
186 dst->allocPixels();
187
188 int width = radius().width();
189 int height = radius().height();
190
191 if (width < 0 || height < 0) {
192 return false;
193 }
194
195 if (width == 0 && height == 0) {
196 src.copyTo(dst, dst->config());
197 return true;
198 }
199
200 SkBitmap temp;
201 temp.setConfig(dst->config(), dst->width(), dst->height());
202 if (!temp.allocPixels()) {
203 return false;
204 }
205
206 if (width > 0 && height > 0) {
207 dilateX(src, &temp, width);
208 dilateY(temp, dst, height);
209 } else if (width > 0) {
210 dilateX(src, dst, width);
211 } else if (height > 0) {
212 dilateY(src, dst, height);
213 }
214 return true;
215}
216
senorblanco@chromium.org302cffb2012-08-01 20:16:34 +0000217GrTexture* SkDilateImageFilter::onFilterImageGPU(GrTexture* src, const SkRect& rect) {
bsalomon@google.comcf8fb1f2012-08-02 14:03:32 +0000218#if SK_SUPPORT_GPU
senorblanco@chromium.org302cffb2012-08-01 20:16:34 +0000219 return src->getContext()->applyMorphology(src, rect,
220 GrContext::kDilate_MorphologyType,
221 radius());
bsalomon@google.comcf8fb1f2012-08-02 14:03:32 +0000222#else
223 SkDEBUGFAIL("Should not call in GPU-less build");
224 return NULL;
225#endif
senorblanco@chromium.org05054f12012-03-02 21:05:45 +0000226}
227
senorblanco@chromium.org302cffb2012-08-01 20:16:34 +0000228GrTexture* SkErodeImageFilter::onFilterImageGPU(GrTexture* src, const SkRect& rect) {
bsalomon@google.comcf8fb1f2012-08-02 14:03:32 +0000229#if SK_SUPPORT_GPU
senorblanco@chromium.org302cffb2012-08-01 20:16:34 +0000230 return src->getContext()->applyMorphology(src, rect,
231 GrContext::kErode_MorphologyType,
232 radius());
bsalomon@google.comcf8fb1f2012-08-02 14:03:32 +0000233#else
234 SkDEBUGFAIL("Should not call in GPU-less build");
235 return NULL;
236#endif
senorblanco@chromium.org05054f12012-03-02 21:05:45 +0000237}
238
239SK_DEFINE_FLATTENABLE_REGISTRAR(SkDilateImageFilter)
240SK_DEFINE_FLATTENABLE_REGISTRAR(SkErodeImageFilter)