OLD | NEW |
1 /* | 1 /* |
2 * Copyright 2011 Google Inc. | 2 * Copyright 2011 Google Inc. |
3 * | 3 * |
4 * Use of this source code is governed by a BSD-style license that can be | 4 * Use of this source code is governed by a BSD-style license that can be |
5 * found in the LICENSE file. | 5 * found in the LICENSE file. |
6 */ | 6 */ |
7 | 7 |
8 #include "SkGpuDevice.h" | 8 #include "SkGpuDevice.h" |
9 | 9 |
10 #include "effects/GrBicubicEffect.h" | 10 #include "effects/GrBicubicEffect.h" |
(...skipping 1122 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1133 } | 1133 } |
1134 | 1134 |
1135 if (iRect->fRight > clamp.fRight) { | 1135 if (iRect->fRight > clamp.fRight) { |
1136 iRect->fRight = clamp.fRight; | 1136 iRect->fRight = clamp.fRight; |
1137 } | 1137 } |
1138 if (iRect->fBottom > clamp.fBottom) { | 1138 if (iRect->fBottom > clamp.fBottom) { |
1139 iRect->fBottom = clamp.fBottom; | 1139 iRect->fBottom = clamp.fBottom; |
1140 } | 1140 } |
1141 } | 1141 } |
1142 | 1142 |
| 1143 static bool has_aligned_samples(const SkRect& srcRect, |
| 1144 const SkRect& transformedRect) { |
| 1145 // detect pixel disalignment |
| 1146 if (SkScalarAbs(SkScalarRoundToScalar(transformedRect.left()) - |
| 1147 transformedRect.left()) < COLOR_BLEED_TOLERANCE && |
| 1148 SkScalarAbs(SkScalarRoundToScalar(transformedRect.top()) - |
| 1149 transformedRect.top()) < COLOR_BLEED_TOLERANCE && |
| 1150 SkScalarAbs(transformedRect.width() - srcRect.width()) < |
| 1151 COLOR_BLEED_TOLERANCE && |
| 1152 SkScalarAbs(transformedRect.height() - srcRect.height()) < |
| 1153 COLOR_BLEED_TOLERANCE) { |
| 1154 return true; |
| 1155 } |
| 1156 return false; |
| 1157 } |
| 1158 |
| 1159 static bool may_color_bleed(const SkRect& srcRect, |
| 1160 const SkRect& transformedRect, |
| 1161 const SkMatrix& m) { |
| 1162 // Only gets called if has_aligned_samples returned false. |
| 1163 // So we can assume that sampling is axis aligned but not texel aligned. |
| 1164 SkASSERT(!has_aligned_samples(srcRect, transformedRect)); |
| 1165 SkRect innerSrcRect(srcRect), innerTransformedRect, |
| 1166 outerTransformedRect(transformedRect); |
| 1167 innerSrcRect.inset(SK_ScalarHalf, SK_ScalarHalf); |
| 1168 m.mapRect(&innerTransformedRect, innerSrcRect); |
| 1169 |
| 1170 // The gap between outerTransformedRect and innerTransformedRect |
| 1171 // represents the projection of the source border area, which is |
| 1172 // problematic for color bleeding. We must check whether any |
| 1173 // destination pixels sample the border area. |
| 1174 outerTransformedRect.inset(COLOR_BLEED_TOLERANCE, COLOR_BLEED_TOLERANCE); |
| 1175 innerTransformedRect.outset(COLOR_BLEED_TOLERANCE, COLOR_BLEED_TOLERANCE); |
| 1176 SkIRect outer, inner; |
| 1177 outerTransformedRect.round(&outer); |
| 1178 innerTransformedRect.round(&inner); |
| 1179 // If the inner and outer rects round to the same result, it means the |
| 1180 // border does not overlap any pixel centers. Yay! |
| 1181 return inner != outer; |
| 1182 } |
| 1183 |
| 1184 static bool needs_texture_domain(const SkBitmap& bitmap, |
| 1185 const SkRect& srcRect, |
| 1186 GrTextureParams ¶ms, |
| 1187 const SkMatrix& contextMatrix, |
| 1188 bool bicubic) { |
| 1189 bool needsTextureDomain = false; |
| 1190 |
| 1191 if (bicubic || params.filterMode() != GrTextureParams::kNone_FilterMode) { |
| 1192 // Need texture domain if drawing a sub rect |
| 1193 needsTextureDomain = srcRect.width() < bitmap.width() || |
| 1194 srcRect.height() < bitmap.height(); |
| 1195 if (!bicubic && needsTextureDomain && contextMatrix.rectStaysRect()) { |
| 1196 // sampling is axis-aligned |
| 1197 SkRect transformedRect; |
| 1198 contextMatrix.mapRect(&transformedRect, srcRect); |
| 1199 |
| 1200 if (has_aligned_samples(srcRect, transformedRect)) { |
| 1201 params.setFilterMode(GrTextureParams::kNone_FilterMode); |
| 1202 needsTextureDomain = false; |
| 1203 } else { |
| 1204 needsTextureDomain = may_color_bleed(srcRect, transformedRect, c
ontextMatrix); |
| 1205 } |
| 1206 } |
| 1207 } |
| 1208 return needsTextureDomain; |
| 1209 } |
| 1210 |
1143 void SkGpuDevice::drawBitmapCommon(const SkDraw& draw, | 1211 void SkGpuDevice::drawBitmapCommon(const SkDraw& draw, |
1144 const SkBitmap& bitmap, | 1212 const SkBitmap& bitmap, |
1145 const SkRect* srcRectPtr, | 1213 const SkRect* srcRectPtr, |
1146 const SkSize* dstSizePtr, | 1214 const SkSize* dstSizePtr, |
1147 const SkPaint& paint, | 1215 const SkPaint& paint, |
1148 SkCanvas::DrawBitmapRectFlags flags) { | 1216 SkCanvas::DrawBitmapRectFlags flags) { |
1149 CHECK_SHOULD_DRAW(draw, false); | 1217 CHECK_SHOULD_DRAW(draw, false); |
1150 | 1218 |
1151 SkRect srcRect; | 1219 SkRect srcRect; |
1152 SkSize dstSize; | 1220 SkSize dstSize; |
(...skipping 118 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1271 int maxTileSize = fContext->getMaxTextureSize() - 2 * tileFilterPad; | 1339 int maxTileSize = fContext->getMaxTextureSize() - 2 * tileFilterPad; |
1272 int tileSize; | 1340 int tileSize; |
1273 | 1341 |
1274 SkIRect clippedSrcRect; | 1342 SkIRect clippedSrcRect; |
1275 if (this->shouldTileBitmap(bitmap, params, srcRectPtr, maxTileSize, &tileSiz
e, | 1343 if (this->shouldTileBitmap(bitmap, params, srcRectPtr, maxTileSize, &tileSiz
e, |
1276 &clippedSrcRect)) { | 1344 &clippedSrcRect)) { |
1277 this->drawTiledBitmap(bitmap, srcRect, clippedSrcRect, params, paint, fl
ags, tileSize, | 1345 this->drawTiledBitmap(bitmap, srcRect, clippedSrcRect, params, paint, fl
ags, tileSize, |
1278 doBicubic); | 1346 doBicubic); |
1279 } else { | 1347 } else { |
1280 // take the simple case | 1348 // take the simple case |
1281 this->internalDrawBitmap(bitmap, srcRect, params, paint, flags, doBicubi
c); | 1349 bool needsTextureDomain = needs_texture_domain(bitmap, |
| 1350 srcRect, |
| 1351 params, |
| 1352 fContext->getMatrix(), |
| 1353 doBicubic); |
| 1354 this->internalDrawBitmap(bitmap, |
| 1355 srcRect, |
| 1356 params, |
| 1357 paint, |
| 1358 flags, |
| 1359 doBicubic, |
| 1360 needsTextureDomain); |
1282 } | 1361 } |
1283 } | 1362 } |
1284 | 1363 |
1285 // Break 'bitmap' into several tiles to draw it since it has already | 1364 // Break 'bitmap' into several tiles to draw it since it has already |
1286 // been determined to be too large to fit in VRAM | 1365 // been determined to be too large to fit in VRAM |
1287 void SkGpuDevice::drawTiledBitmap(const SkBitmap& bitmap, | 1366 void SkGpuDevice::drawTiledBitmap(const SkBitmap& bitmap, |
1288 const SkRect& srcRect, | 1367 const SkRect& srcRect, |
1289 const SkIRect& clippedSrcIRect, | 1368 const SkIRect& clippedSrcIRect, |
1290 const GrTextureParams& params, | 1369 const GrTextureParams& params, |
1291 const SkPaint& paint, | 1370 const SkPaint& paint, |
(...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1342 // not bleed across the original clamped edges) | 1421 // not bleed across the original clamped edges) |
1343 srcRect.roundOut(&iClampRect); | 1422 srcRect.roundOut(&iClampRect); |
1344 } | 1423 } |
1345 int outset = bicubic ? GrBicubicEffect::kFilterTexelPad : 1; | 1424 int outset = bicubic ? GrBicubicEffect::kFilterTexelPad : 1; |
1346 clamped_outset_with_offset(&iTileR, outset, &offset, iClampRect)
; | 1425 clamped_outset_with_offset(&iTileR, outset, &offset, iClampRect)
; |
1347 } | 1426 } |
1348 | 1427 |
1349 if (bitmap.extractSubset(&tmpB, iTileR)) { | 1428 if (bitmap.extractSubset(&tmpB, iTileR)) { |
1350 // now offset it to make it "local" to our tmp bitmap | 1429 // now offset it to make it "local" to our tmp bitmap |
1351 tileR.offset(-offset.fX, -offset.fY); | 1430 tileR.offset(-offset.fX, -offset.fY); |
1352 | 1431 GrTextureParams paramsTemp = params; |
1353 this->internalDrawBitmap(tmpB, tileR, params, paint, flags, bicu
bic); | 1432 bool needsTextureDomain = needs_texture_domain(bitmap, |
| 1433 srcRect, |
| 1434 paramsTemp, |
| 1435 fContext->getMatr
ix(), |
| 1436 bicubic); |
| 1437 this->internalDrawBitmap(tmpB, |
| 1438 tileR, |
| 1439 paramsTemp, |
| 1440 paint, |
| 1441 flags, |
| 1442 bicubic, |
| 1443 needsTextureDomain); |
1354 } | 1444 } |
1355 } | 1445 } |
1356 } | 1446 } |
1357 } | 1447 } |
1358 | 1448 |
1359 static bool has_aligned_samples(const SkRect& srcRect, | |
1360 const SkRect& transformedRect) { | |
1361 // detect pixel disalignment | |
1362 if (SkScalarAbs(SkScalarRoundToScalar(transformedRect.left()) - | |
1363 transformedRect.left()) < COLOR_BLEED_TOLERANCE && | |
1364 SkScalarAbs(SkScalarRoundToScalar(transformedRect.top()) - | |
1365 transformedRect.top()) < COLOR_BLEED_TOLERANCE && | |
1366 SkScalarAbs(transformedRect.width() - srcRect.width()) < | |
1367 COLOR_BLEED_TOLERANCE && | |
1368 SkScalarAbs(transformedRect.height() - srcRect.height()) < | |
1369 COLOR_BLEED_TOLERANCE) { | |
1370 return true; | |
1371 } | |
1372 return false; | |
1373 } | |
1374 | |
1375 static bool may_color_bleed(const SkRect& srcRect, | |
1376 const SkRect& transformedRect, | |
1377 const SkMatrix& m) { | |
1378 // Only gets called if has_aligned_samples returned false. | |
1379 // So we can assume that sampling is axis aligned but not texel aligned. | |
1380 SkASSERT(!has_aligned_samples(srcRect, transformedRect)); | |
1381 SkRect innerSrcRect(srcRect), innerTransformedRect, | |
1382 outerTransformedRect(transformedRect); | |
1383 innerSrcRect.inset(SK_ScalarHalf, SK_ScalarHalf); | |
1384 m.mapRect(&innerTransformedRect, innerSrcRect); | |
1385 | |
1386 // The gap between outerTransformedRect and innerTransformedRect | |
1387 // represents the projection of the source border area, which is | |
1388 // problematic for color bleeding. We must check whether any | |
1389 // destination pixels sample the border area. | |
1390 outerTransformedRect.inset(COLOR_BLEED_TOLERANCE, COLOR_BLEED_TOLERANCE); | |
1391 innerTransformedRect.outset(COLOR_BLEED_TOLERANCE, COLOR_BLEED_TOLERANCE); | |
1392 SkIRect outer, inner; | |
1393 outerTransformedRect.round(&outer); | |
1394 innerTransformedRect.round(&inner); | |
1395 // If the inner and outer rects round to the same result, it means the | |
1396 // border does not overlap any pixel centers. Yay! | |
1397 return inner != outer; | |
1398 } | |
1399 | |
1400 | 1449 |
1401 /* | 1450 /* |
1402 * This is called by drawBitmap(), which has to handle images that may be too | 1451 * This is called by drawBitmap(), which has to handle images that may be too |
1403 * large to be represented by a single texture. | 1452 * large to be represented by a single texture. |
1404 * | 1453 * |
1405 * internalDrawBitmap assumes that the specified bitmap will fit in a texture | 1454 * internalDrawBitmap assumes that the specified bitmap will fit in a texture |
1406 * and that non-texture portion of the GrPaint has already been setup. | 1455 * and that non-texture portion of the GrPaint has already been setup. |
1407 */ | 1456 */ |
1408 void SkGpuDevice::internalDrawBitmap(const SkBitmap& bitmap, | 1457 void SkGpuDevice::internalDrawBitmap(const SkBitmap& bitmap, |
1409 const SkRect& srcRect, | 1458 const SkRect& srcRect, |
1410 const GrTextureParams& params, | 1459 const GrTextureParams& params, |
1411 const SkPaint& paint, | 1460 const SkPaint& paint, |
1412 SkCanvas::DrawBitmapRectFlags flags, | 1461 SkCanvas::DrawBitmapRectFlags flags, |
1413 bool bicubic) { | 1462 bool bicubic, |
| 1463 bool needsTextureDomain) { |
1414 SkASSERT(bitmap.width() <= fContext->getMaxTextureSize() && | 1464 SkASSERT(bitmap.width() <= fContext->getMaxTextureSize() && |
1415 bitmap.height() <= fContext->getMaxTextureSize()); | 1465 bitmap.height() <= fContext->getMaxTextureSize()); |
1416 | 1466 |
1417 GrTexture* texture; | 1467 GrTexture* texture; |
1418 SkAutoCachedTexture act(this, bitmap, ¶ms, &texture); | 1468 SkAutoCachedTexture act(this, bitmap, ¶ms, &texture); |
1419 if (NULL == texture) { | 1469 if (NULL == texture) { |
1420 return; | 1470 return; |
1421 } | 1471 } |
1422 | 1472 |
1423 SkRect dstRect = {0, 0, srcRect.width(), srcRect.height() }; | 1473 SkRect dstRect = {0, 0, srcRect.width(), srcRect.height() }; |
1424 SkRect paintRect; | 1474 SkRect paintRect; |
1425 SkScalar wInv = SkScalarInvert(SkIntToScalar(texture->width())); | 1475 SkScalar wInv = SkScalarInvert(SkIntToScalar(texture->width())); |
1426 SkScalar hInv = SkScalarInvert(SkIntToScalar(texture->height())); | 1476 SkScalar hInv = SkScalarInvert(SkIntToScalar(texture->height())); |
1427 paintRect.setLTRB(SkScalarMul(srcRect.fLeft, wInv), | 1477 paintRect.setLTRB(SkScalarMul(srcRect.fLeft, wInv), |
1428 SkScalarMul(srcRect.fTop, hInv), | 1478 SkScalarMul(srcRect.fTop, hInv), |
1429 SkScalarMul(srcRect.fRight, wInv), | 1479 SkScalarMul(srcRect.fRight, wInv), |
1430 SkScalarMul(srcRect.fBottom, hInv)); | 1480 SkScalarMul(srcRect.fBottom, hInv)); |
1431 | 1481 |
1432 bool needsTextureDomain = false; | |
1433 if (!(flags & SkCanvas::kBleed_DrawBitmapRectFlag) && | |
1434 (bicubic || params.filterMode() != GrTextureParams::kNone_FilterMode)) { | |
1435 // Need texture domain if drawing a sub rect | |
1436 needsTextureDomain = srcRect.width() < bitmap.width() || | |
1437 srcRect.height() < bitmap.height(); | |
1438 if (!bicubic && needsTextureDomain && fContext->getMatrix().rectStaysRec
t()) { | |
1439 const SkMatrix& matrix = fContext->getMatrix(); | |
1440 // sampling is axis-aligned | |
1441 SkRect transformedRect; | |
1442 matrix.mapRect(&transformedRect, srcRect); | |
1443 | |
1444 if (has_aligned_samples(srcRect, transformedRect)) { | |
1445 // We could also turn off filtering here (but we already did a c
ache lookup with | |
1446 // params). | |
1447 needsTextureDomain = false; | |
1448 } else { | |
1449 needsTextureDomain = may_color_bleed(srcRect, transformedRect, m
atrix); | |
1450 } | |
1451 } | |
1452 } | |
1453 | |
1454 SkRect textureDomain = SkRect::MakeEmpty(); | 1482 SkRect textureDomain = SkRect::MakeEmpty(); |
1455 SkAutoTUnref<GrEffectRef> effect; | 1483 SkAutoTUnref<GrEffectRef> effect; |
1456 if (needsTextureDomain) { | 1484 if (needsTextureDomain && !(flags & SkCanvas::kBleed_DrawBitmapRectFlag)) { |
1457 // Use a constrained texture domain to avoid color bleeding | 1485 // Use a constrained texture domain to avoid color bleeding |
1458 SkScalar left, top, right, bottom; | 1486 SkScalar left, top, right, bottom; |
1459 if (srcRect.width() > SK_Scalar1) { | 1487 if (srcRect.width() > SK_Scalar1) { |
1460 SkScalar border = SK_ScalarHalf / texture->width(); | 1488 SkScalar border = SK_ScalarHalf / texture->width(); |
1461 left = paintRect.left() + border; | 1489 left = paintRect.left() + border; |
1462 right = paintRect.right() - border; | 1490 right = paintRect.right() - border; |
1463 } else { | 1491 } else { |
1464 left = right = SkScalarHalf(paintRect.left() + paintRect.right()); | 1492 left = right = SkScalarHalf(paintRect.left() + paintRect.right()); |
1465 } | 1493 } |
1466 if (srcRect.height() > SK_Scalar1) { | 1494 if (srcRect.height() > SK_Scalar1) { |
(...skipping 625 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2092 GrCachedLayer* layer = fContext->getLayerCache()->findLayerOrCreate(pict
ure, i); | 2120 GrCachedLayer* layer = fContext->getLayerCache()->findLayerOrCreate(pict
ure, i); |
2093 | 2121 |
2094 if (NULL != layer->getTexture()) { | 2122 if (NULL != layer->getTexture()) { |
2095 fContext->unlockScratchTexture(layer->getTexture()); | 2123 fContext->unlockScratchTexture(layer->getTexture()); |
2096 layer->setTexture(NULL); | 2124 layer->setTexture(NULL); |
2097 } | 2125 } |
2098 } | 2126 } |
2099 | 2127 |
2100 return true; | 2128 return true; |
2101 } | 2129 } |
OLD | NEW |