OLD | NEW |
---|---|
1 | 1 |
2 /* | 2 /* |
3 * Copyright 2006 The Android Open Source Project | 3 * Copyright 2006 The Android Open Source Project |
4 * | 4 * |
5 * Use of this source code is governed by a BSD-style license that can be | 5 * Use of this source code is governed by a BSD-style license that can be |
6 * found in the LICENSE file. | 6 * found in the LICENSE file. |
7 */ | 7 */ |
8 | 8 |
9 | 9 |
10 #include "SkBlurMask.h" | 10 #include "SkBlurMask.h" |
(...skipping 1182 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1193 all the time, we actually fill in the profile pre-inverted | 1193 all the time, we actually fill in the profile pre-inverted |
1194 (already done 255-x). | 1194 (already done 255-x). |
1195 | 1195 |
1196 The function returns the size of the array allocated for the | 1196 The function returns the size of the array allocated for the |
1197 profile. It's the responsibility of the caller to delete the | 1197 profile. It's the responsibility of the caller to delete the |
1198 memory returned in profile_out. | 1198 memory returned in profile_out. |
1199 */ | 1199 */ |
1200 | 1200 |
1201 static int compute_profile(SkScalar radius, unsigned int **profile_out) { | 1201 static int compute_profile(SkScalar radius, unsigned int **profile_out) { |
1202 int size = SkScalarRoundToInt(radius * 3); | 1202 int size = SkScalarRoundToInt(radius * 3); |
1203 int center = size >> 1; | 1203 |
1204 if (profile_out) { | |
1205 int center = size >> 1; | |
1206 unsigned int *profile = SkNEW_ARRAY(unsigned int, size); | |
1204 | 1207 |
1205 unsigned int *profile = SkNEW_ARRAY(unsigned int, size); | 1208 float invr = 1.f/radius; |
1206 | 1209 |
1207 float invr = 1.f/radius; | 1210 profile[0] = 255; |
1211 for (int x = 1 ; x < size ; ++x) { | |
1212 float scaled_x = (center - x - .5f) * invr; | |
1213 float gi = gaussianIntegral(scaled_x); | |
1214 profile[x] = 255 - (uint8_t) (255.f * gi); | |
1215 } | |
1208 | 1216 |
1209 profile[0] = 255; | 1217 *profile_out = profile; |
1210 for (int x = 1 ; x < size ; ++x) { | |
1211 float scaled_x = (center - x - .5f) * invr; | |
1212 float gi = gaussianIntegral(scaled_x); | |
1213 profile[x] = 255 - (uint8_t) (255.f * gi); | |
1214 } | 1218 } |
1215 | |
1216 *profile_out = profile; | |
1217 return size; | 1219 return size; |
1218 } | 1220 } |
1219 | 1221 |
1220 // TODO MAYBE: Maintain a profile cache to avoid recomputing this for | 1222 // TODO MAYBE: Maintain a profile cache to avoid recomputing this for |
1221 // commonly used radii. Consider baking some of the most common blur radii | 1223 // commonly used radii. Consider baking some of the most common blur radii |
1222 // directly in as static data? | 1224 // directly in as static data? |
1223 | 1225 |
1224 // Implementation adapted from Michael Herf's approach: | 1226 // Implementation adapted from Michael Herf's approach: |
1225 // http://stereopsis.com/shadowrect/ | 1227 // http://stereopsis.com/shadowrect/ |
1226 | 1228 |
1227 static inline unsigned int profile_lookup( unsigned int *profile, int loc, int b lurred_width, int sharp_width ) { | 1229 static inline unsigned int profile_lookup( unsigned int *profile, int loc, int b lurred_width, int sharp_width ) { |
1228 int dx = SkAbs32(((loc << 1) + 1) - blurred_width) - sharp_width; // how far are we from the original edge? | 1230 int dx = SkAbs32(((loc << 1) + 1) - blurred_width) - sharp_width; // how far are we from the original edge? |
1229 int ox = dx >> 1; | 1231 int ox = dx >> 1; |
1230 if (ox < 0) { | 1232 if (ox < 0) { |
1231 ox = 0; | 1233 ox = 0; |
1232 } | 1234 } |
1233 | 1235 |
1234 return profile[ox]; | 1236 return profile[ox]; |
1235 } | 1237 } |
1236 | 1238 |
1237 bool SkBlurMask::BlurRect(SkMask *dst, const SkRect &src, | 1239 bool SkBlurMask::BlurRect(SkMask *dst, const SkRect &src, |
1238 SkScalar provided_radius, Style style, | 1240 SkScalar provided_radius, Style style, |
1239 SkIPoint *margin) { | 1241 SkIPoint *margin, bool perform_blur) { |
1240 int profile_size; | 1242 int profile_size; |
1241 unsigned int *profile; | 1243 unsigned int *profile = NULL; |
1242 | 1244 |
1243 float radius = SkScalarToFloat( SkScalarMul( provided_radius, kBlurRadiusFud geFactor ) ); | 1245 float radius = SkScalarToFloat( SkScalarMul( provided_radius, kBlurRadiusFud geFactor ) ); |
1244 | 1246 |
1245 // adjust blur radius to match interpretation from boxfilter code | 1247 // adjust blur radius to match interpretation from boxfilter code |
1246 radius = (radius + .5f) *2.f; | 1248 radius = (radius + .5f) *2.f; |
bungeman-skia
2013/03/11 19:47:41
My eyes... need a space after *
| |
1247 | 1249 |
1248 profile_size = compute_profile( radius, &profile ); | 1250 profile_size = compute_profile( radius, perform_blur ? &profile : NULL ); |
bungeman-skia
2013/03/11 19:47:41
It feels awkward to have this check here and anoth
| |
1249 | 1251 |
1250 SkAutoTDeleteArray<unsigned int> ada(profile); | 1252 SkAutoTDeleteArray<unsigned int> ada(profile); |
1251 | 1253 |
1252 int pad = profile_size/2; | 1254 int pad = profile_size/2; |
1253 if (margin) { | 1255 if (margin) { |
1254 margin->set( pad, pad ); | 1256 margin->set( pad, pad ); |
1255 } | 1257 } |
1256 | 1258 |
1257 int shadow_left = -pad; | 1259 int shadow_left = -pad; |
1258 int shadow_top = -pad; | 1260 int shadow_top = -pad; |
1259 int shadow_right = (int)(src.width()) + pad; | 1261 int shadow_right = (int)(src.width()) + pad; |
1260 int shadow_bottom = (int)(src.height()) + pad; | 1262 int shadow_bottom = (int)(src.height()) + pad; |
1261 | 1263 |
1262 dst->fBounds.set(shadow_left, shadow_top, shadow_right, shadow_bottom); | 1264 dst->fBounds.set(shadow_left + src.fLeft, shadow_top + src.fTop, shadow_righ t + src.fRight, shadow_bottom + src.fBottom); |
bungeman-skia
2013/03/11 19:47:41
Nit: This line got really long. A few more below.
| |
1263 | 1265 |
1264 dst->fRowBytes = dst->fBounds.width(); | 1266 dst->fRowBytes = dst->fBounds.width(); |
1265 dst->fFormat = SkMask::kA8_Format; | 1267 dst->fFormat = SkMask::kA8_Format; |
1266 dst->fImage = NULL; | 1268 dst->fImage = NULL; |
1267 | 1269 |
1268 size_t dstSize = dst->computeImageSize(); | |
1269 if (0 == dstSize) { | |
1270 return false; // too big to allocate, abort | |
1271 } | |
1272 | |
1273 int sw = SkScalarFloorToInt(src.width()); | 1270 int sw = SkScalarFloorToInt(src.width()); |
1274 int sh = SkScalarFloorToInt(src.height()); | 1271 int sh = SkScalarFloorToInt(src.height()); |
1272 | |
1273 if (perform_blur) { | |
bungeman-skia
2013/03/11 19:47:41
Can we put the else clause under !perform_blur and
| |
1274 size_t dstSize = dst->computeImageSize(); | |
1275 if (0 == dstSize) { | |
1276 return false; // too big to allocate, abort | |
1277 } | |
1275 | 1278 |
1276 uint8_t* dp = SkMask::AllocImage(dstSize); | 1279 uint8_t* dp = SkMask::AllocImage(dstSize); |
1277 | 1280 |
1278 dst->fImage = dp; | 1281 dst->fImage = dp; |
1279 | 1282 |
1280 int dstHeight = dst->fBounds.height(); | 1283 int dstHeight = dst->fBounds.height(); |
1281 int dstWidth = dst->fBounds.width(); | 1284 int dstWidth = dst->fBounds.width(); |
1282 | 1285 |
1283 // nearest odd number less than the profile size represents the center | 1286 // nearest odd number less than the profile size represents the center |
1284 // of the (2x scaled) profile | 1287 // of the (2x scaled) profile |
1285 int center = ( profile_size & ~1 ) - 1; | 1288 int center = ( profile_size & ~1 ) - 1; |
1286 | 1289 |
1287 int w = sw - center; | 1290 int w = sw - center; |
1288 int h = sh - center; | 1291 int h = sh - center; |
1289 | 1292 |
1290 uint8_t *outptr = dp; | 1293 uint8_t *outptr = dp; |
1291 | 1294 |
1292 SkAutoTMalloc<uint8_t> horizontalScanline(dstWidth); | 1295 SkAutoTMalloc<uint8_t> horizontalScanline(dstWidth); |
1293 | 1296 |
1294 for (int x = 0 ; x < dstWidth ; ++x) { | 1297 for (int x = 0 ; x < dstWidth ; ++x) { |
1295 if (profile_size <= sw) { | 1298 if (profile_size <= sw) { |
1296 horizontalScanline[x] = profile_lookup(profile, x, dstWidth, w); | 1299 horizontalScanline[x] = profile_lookup(profile, x, dstWidth, w); |
1297 } else { | 1300 } else { |
1298 float span = float(sw)/radius; | 1301 float span = float(sw)/radius; |
1299 float giX = 1.5f - (x+.5f)/radius; | 1302 float giX = 1.5f - (x+.5f)/radius; |
1300 horizontalScanline[x] = (uint8_t) (255 * (gaussianIntegral(giX) - ga ussianIntegral(giX + span))); | 1303 horizontalScanline[x] = (uint8_t) (255 * (gaussianIntegral(giX) - gaussianIntegral(giX + span))); |
1304 } | |
1305 } | |
1306 | |
1307 for (int y = 0 ; y < dstHeight ; ++y) { | |
1308 unsigned int profile_y; | |
1309 if (profile_size <= sh) { | |
1310 profile_y = profile_lookup(profile, y, dstHeight, h); | |
1311 } else { | |
1312 float span = float(sh)/radius; | |
1313 float giY = 1.5f - (y+.5f)/radius; | |
1314 profile_y = (uint8_t) (255 * (gaussianIntegral(giY) - gaussianIn tegral(giY + span))); | |
1315 } | |
1316 | |
1317 for (int x = 0 ; x < dstWidth ; x++) { | |
1318 unsigned int maskval = SkMulDiv255Round(horizontalScanline[x], p rofile_y); | |
1319 *(outptr++) = maskval; | |
1320 } | |
1321 } | |
1322 | |
1323 if (style == kInner_Style) { | |
1324 // now we allocate the "real" dst, mirror the size of src | |
1325 size_t srcSize = (size_t)(src.width() * src.height()); | |
1326 if (0 == srcSize) { | |
1327 return false; // too big to allocate, abort | |
1328 } | |
1329 dst->fImage = SkMask::AllocImage(srcSize); | |
1330 for (int y = 0 ; y < sh ; y++) { | |
1331 uint8_t *blur_scanline = dp + (y+pad)*dstWidth + pad; | |
1332 uint8_t *inner_scanline = dst->fImage + y*sw; | |
1333 memcpy(inner_scanline, blur_scanline, sw); | |
1334 } | |
1335 SkMask::FreeImage(dp); | |
1336 | |
1337 dst->fBounds.set(0, 0, sw, sh); // restore trimmed bounds | |
1338 dst->fRowBytes = sw; | |
1339 | |
1340 } else if (style == kOuter_Style) { | |
1341 for (int y = pad ; y < dstHeight-pad ; y++) { | |
1342 uint8_t *dst_scanline = dp + y*dstWidth + pad; | |
1343 memset(dst_scanline, 0, sw); | |
1344 } | |
1345 } | |
1346 // normal and solid styles are the same for analytic rect blurs, so don' t | |
1347 // need to handle solid specially. | |
1348 } else { | |
1349 if (style == kInner_Style) { | |
1350 dst->fBounds.set(0, 0, sw, sh); // restore trimmed bounds | |
1351 dst->fRowBytes = sw; | |
1301 } | 1352 } |
1302 } | 1353 } |
1303 | 1354 |
1304 for (int y = 0 ; y < dstHeight ; ++y) { | |
1305 unsigned int profile_y; | |
1306 if (profile_size <= sh) { | |
1307 profile_y = profile_lookup(profile, y, dstHeight, h); | |
1308 } else { | |
1309 float span = float(sh)/radius; | |
1310 float giY = 1.5f - (y+.5f)/radius; | |
1311 profile_y = (uint8_t) (255 * (gaussianIntegral(giY) - gaussianIntegr al(giY + span))); | |
1312 } | |
1313 | |
1314 for (int x = 0 ; x < dstWidth ; x++) { | |
1315 unsigned int maskval = SkMulDiv255Round(horizontalScanline[x], profi le_y); | |
1316 *(outptr++) = maskval; | |
1317 } | |
1318 } | |
1319 | |
1320 if (style == kInner_Style) { | |
1321 // now we allocate the "real" dst, mirror the size of src | |
1322 size_t srcSize = (size_t)(src.width() * src.height()); | |
1323 if (0 == srcSize) { | |
1324 return false; // too big to allocate, abort | |
1325 } | |
1326 dst->fImage = SkMask::AllocImage(srcSize); | |
1327 for (int y = 0 ; y < sh ; y++) { | |
1328 uint8_t *blur_scanline = dp + (y+pad)*dstWidth + pad; | |
1329 uint8_t *inner_scanline = dst->fImage + y*sw; | |
1330 memcpy(inner_scanline, blur_scanline, sw); | |
1331 } | |
1332 SkMask::FreeImage(dp); | |
1333 | |
1334 dst->fBounds.set(0, 0, sw, sh); // restore trimmed bounds | |
1335 dst->fRowBytes = sw; | |
1336 | |
1337 } else if (style == kOuter_Style) { | |
1338 for (int y = pad ; y < dstHeight-pad ; y++) { | |
1339 uint8_t *dst_scanline = dp + y*dstWidth + pad; | |
1340 memset(dst_scanline, 0, sw); | |
1341 } | |
1342 } | |
1343 // normal and solid styles are the same for analytic rect blurs, so don't | |
1344 // need to handle solid specially. | |
1345 | |
1346 return true; | 1355 return true; |
1347 } | 1356 } |
1348 | 1357 |
1349 // The "simple" blur is a direct implementation of separable convolution with a discrete | 1358 // The "simple" blur is a direct implementation of separable convolution with a discrete |
1350 // gaussian kernel. It's "ground truth" in a sense; too slow to be used, but ve ry | 1359 // gaussian kernel. It's "ground truth" in a sense; too slow to be used, but ve ry |
1351 // useful for correctness comparisons. | 1360 // useful for correctness comparisons. |
1352 | 1361 |
1353 bool SkBlurMask::BlurGroundTruth(SkMask* dst, const SkMask& src, SkScalar provid ed_radius, | 1362 bool SkBlurMask::BlurGroundTruth(SkMask* dst, const SkMask& src, SkScalar provid ed_radius, |
1354 Style style, SkIPoint* margin) { | 1363 Style style, SkIPoint* margin) { |
1355 | 1364 |
(...skipping 132 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1488 (void)autoCall.detach(); | 1497 (void)autoCall.detach(); |
1489 } | 1498 } |
1490 | 1499 |
1491 if (style == kInner_Style) { | 1500 if (style == kInner_Style) { |
1492 dst->fBounds = src.fBounds; // restore trimmed bounds | 1501 dst->fBounds = src.fBounds; // restore trimmed bounds |
1493 dst->fRowBytes = src.fRowBytes; | 1502 dst->fRowBytes = src.fRowBytes; |
1494 } | 1503 } |
1495 | 1504 |
1496 return true; | 1505 return true; |
1497 } | 1506 } |
OLD | NEW |