Chromium Code Reviews| 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 |