/// Image's dimensions used for drawing. X = left, Y = bottom, Z = right, W = top. private Vector4 GetDrawingDimensions(bool shouldPreserveAspect) { var padding = activeSprite == null ? Vector4.zero : Sprites.DataUtility.GetPadding(activeSprite); var size = activeSprite == null ? Vector2.zero : new Vector2(activeSprite.rect.width, activeSprite.rect.height);
Rect r = GetPixelAdjustedRect(); // Debug.Log(string.Format("r:{2}, size:{0}, padding:{1}", size, padding, r));
int spriteW = Mathf.RoundToInt(size.x); int spriteH = Mathf.RoundToInt(size.y);
var v = new Vector4( padding.x / spriteW, padding.y / spriteH, (spriteW - padding.z) / spriteW, (spriteH - padding.w) / spriteH);
if (shouldPreserveAspect && size.sqrMagnitude > 0.0f) { var spriteRatio = size.x / size.y; var rectRatio = r.width / r.height;
for (int i = 0; i < 4; ++i) { s_VertScratch[i].x += rect.x; s_VertScratch[i].y += rect.y; }
s_UVScratch[0] = new Vector2(outer.x, outer.y); s_UVScratch[1] = new Vector2(inner.x, inner.y); s_UVScratch[2] = new Vector2(inner.z, inner.w); s_UVScratch[3] = new Vector2(outer.z, outer.w);
toFill.Clear();
for (int x = 0; x < 3; ++x) { int x2 = x + 1;
for (int y = 0; y < 3; ++y) { if (!m_FillCenter && x == 1 && y == 1) continue;
int y2 = y + 1;
AddQuad(toFill, new Vector2(s_VertScratch[x].x, s_VertScratch[y].y), new Vector2(s_VertScratch[x2].x, s_VertScratch[y2].y), color, new Vector2(s_UVScratch[x].x, s_UVScratch[y].y), new Vector2(s_UVScratch[x2].x, s_UVScratch[y2].y)); } } }
Vector4 GetAdjustedBorders(Vector4 border, Rect rect) { for (int axis = 0; axis <= 1; axis++) { // If the rect is smaller than the combined borders, then there's not room for the borders at their normal size. // In order to avoid artefacts with overlapping borders, we scale the borders down to fit. float combinedBorders = border[axis] + border[axis + 2]; if (rect.size[axis] < combinedBorders && combinedBorders != 0) { float borderScaleRatio = rect.size[axis] / combinedBorders; border[axis] *= borderScaleRatio; border[axis + 2] *= borderScaleRatio; } } return border; }
接下来是重点部分了,填充s_VertScratch和s_UVScratch,摘录之前的代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
s_VertScratch[0] = new Vector2(padding.x, padding.y); s_VertScratch[3] = new Vector2(rect.width - padding.z, rect.height - padding.w);
for (int i = 0; i < 4; ++i) { s_VertScratch[i].x += rect.x; s_VertScratch[i].y += rect.y; }
s_UVScratch[0] = new Vector2(outer.x, outer.y); s_UVScratch[1] = new Vector2(inner.x, inner.y); s_UVScratch[2] = new Vector2(inner.z, inner.w); s_UVScratch[3] = new Vector2(outer.z, outer.w);
var uvMin = new Vector2(inner.x, inner.y); var uvMax = new Vector2(inner.z, inner.w);
// Min to max max range for tiled region in coordinates relative to lower left corner. float xMin = border.x; float xMax = rect.width - border.z; float yMin = border.y; float yMax = rect.height - border.w;
toFill.Clear(); var clipped = uvMax;
// if either width is zero we cant tile so just assume it was the full width. if (tileWidth <= 0) tileWidth = xMax - xMin;
if (tileHeight <= 0) tileHeight = yMax - yMin;
if (activeSprite != null && (hasBorder || activeSprite.packed || activeSprite.texture.wrapMode != TextureWrapMode.Repeat)) { // Sprite has border, or is not in repeat mode, or cannot be repeated because of packing. // We cannot use texture tiling so we will generate a mesh of quads to tile the texture.
// Evaluate how many vertices we will generate. Limit this number to something sane, // especially since meshes can not have more than 65000 vertices.
int nTilesW = 0; int nTilesH = 0; if (m_FillCenter) { nTilesW = (int)Math.Ceiling((xMax - xMin) / tileWidth); nTilesH = (int)Math.Ceiling((yMax - yMin) / tileHeight);
int nVertices = 0; if (hasBorder) { nVertices = (nTilesW + 2) * (nTilesH + 2) * 4; // 4 vertices per tile } else { nVertices = nTilesW * nTilesH * 4; // 4 vertices per tile }
if (nVertices > 65000) { Debug.LogError("Too many sprite tiles on Image \"" + name + "\". The tile size will be increased. To remove the limit on the number of tiles, convert the Sprite to an Advanced texture, remove the borders, clear the Packing tag and set the Wrap mode to Repeat.", this);
double maxTiles = 65000.0 / 4.0; // Max number of vertices is 65000; 4 vertices per tile. double imageRatio; if (hasBorder) { imageRatio = (nTilesW + 2.0) / (nTilesH + 2.0); } else { imageRatio = (double)nTilesW / nTilesH; }
nTilesW = (int)Math.Floor(targetTilesW); nTilesH = (int)Math.Floor(targetTilesH); tileWidth = (xMax - xMin) / nTilesW; tileHeight = (yMax - yMin) / nTilesH; } } else { if (hasBorder) { // Texture on the border is repeated only in one direction. nTilesW = (int)Math.Ceiling((xMax - xMin) / tileWidth); nTilesH = (int)Math.Ceiling((yMax - yMin) / tileHeight); int nVertices = (nTilesH + nTilesW + 2 /*corners*/) * 2 /*sides*/ * 4 /*vertices per tile*/; if (nVertices > 65000) { Debug.LogError("Too many sprite tiles on Image \"" + name + "\". The tile size will be increased. To remove the limit on the number of tiles, convert the Sprite to an Advanced texture, remove the borders, clear the Packing tag and set the Wrap mode to Repeat.", this);
double maxTiles = 65000.0 / 4.0; // Max number of vertices is 65000; 4 vertices per tile. double imageRatio = (double)nTilesW / nTilesH; double targetTilesW = (maxTiles - 4 /*corners*/) / (2 * (1.0 + imageRatio)); double targetTilesH = targetTilesW * imageRatio;
if (m_FillCenter) { // TODO: we could share vertices between quads. If vertex sharing is implemented. update the computation for the number of vertices accordingly. for (int j = 0; j < nTilesH; j++) { float y1 = yMin + j * tileHeight; float y2 = yMin + (j + 1) * tileHeight; if (y2 > yMax) { clipped.y = uvMin.y + (uvMax.y - uvMin.y) * (yMax - y1) / (y2 - y1); y2 = yMax; } clipped.x = uvMax.x; for (int i = 0; i < nTilesW; i++) { float x1 = xMin + i * tileWidth; float x2 = xMin + (i + 1) * tileWidth; if (x2 > xMax) { clipped.x = uvMin.x + (uvMax.x - uvMin.x) * (xMax - x1) / (x2 - x1); x2 = xMax; } AddQuad(toFill, new Vector2(x1, y1) + rect.position, new Vector2(x2, y2) + rect.position, color, uvMin, clipped); } } } if (hasBorder) { clipped = uvMax; for (int j = 0; j < nTilesH; j++) { float y1 = yMin + j * tileHeight; float y2 = yMin + (j + 1) * tileHeight; if (y2 > yMax) { clipped.y = uvMin.y + (uvMax.y - uvMin.y) * (yMax - y1) / (y2 - y1); y2 = yMax; } AddQuad(toFill, new Vector2(0, y1) + rect.position, new Vector2(xMin, y2) + rect.position, color, new Vector2(outer.x, uvMin.y), new Vector2(uvMin.x, clipped.y)); AddQuad(toFill, new Vector2(xMax, y1) + rect.position, new Vector2(rect.width, y2) + rect.position, color, new Vector2(uvMax.x, uvMin.y), new Vector2(outer.z, clipped.y)); }
// Bottom and top tiled border clipped = uvMax; for (int i = 0; i < nTilesW; i++) { float x1 = xMin + i * tileWidth; float x2 = xMin + (i + 1) * tileWidth; if (x2 > xMax) { clipped.x = uvMin.x + (uvMax.x - uvMin.x) * (xMax - x1) / (x2 - x1); x2 = xMax; } AddQuad(toFill, new Vector2(x1, 0) + rect.position, new Vector2(x2, yMin) + rect.position, color, new Vector2(uvMin.x, outer.y), new Vector2(clipped.x, uvMin.y)); AddQuad(toFill, new Vector2(x1, yMax) + rect.position, new Vector2(x2, rect.height) + rect.position, color, new Vector2(uvMin.x, uvMax.y), new Vector2(clipped.x, outer.w)); }
// Corners AddQuad(toFill, new Vector2(0, 0) + rect.position, new Vector2(xMin, yMin) + rect.position, color, new Vector2(outer.x, outer.y), new Vector2(uvMin.x, uvMin.y)); AddQuad(toFill, new Vector2(xMax, 0) + rect.position, new Vector2(rect.width, yMin) + rect.position, color, new Vector2(uvMax.x, outer.y), new Vector2(outer.z, uvMin.y)); AddQuad(toFill, new Vector2(0, yMax) + rect.position, new Vector2(xMin, rect.height) + rect.position, color, new Vector2(outer.x, uvMax.y), new Vector2(uvMin.x, outer.w)); AddQuad(toFill, new Vector2(xMax, yMax) + rect.position, new Vector2(rect.width, rect.height) + rect.position, color, new Vector2(uvMax.x, uvMax.y), new Vector2(outer.z, outer.w)); } } else { // Texture has no border, is in repeat mode and not packed. Use texture tiling. Vector2 uvScale = new Vector2((xMax - xMin) / tileWidth, (yMax - yMin) / tileHeight);
if (m_FillCenter) { AddQuad(toFill, new Vector2(xMin, yMin) + rect.position, new Vector2(xMax, yMax) + rect.position, color, Vector2.Scale(uvMin, uvScale), Vector2.Scale(uvMax, uvScale)); } } }
if (hasBorder) { clipped = uvMax; for (int j = 0; j < nTilesH; j++) { float y1 = yMin + j * tileHeight; float y2 = yMin + (j + 1) * tileHeight; if (y2 > yMax) { clipped.y = uvMin.y + (uvMax.y - uvMin.y) * (yMax - y1) / (y2 - y1); y2 = yMax; } AddQuad(toFill, new Vector2(0, y1) + rect.position, new Vector2(xMin, y2) + rect.position, color, new Vector2(outer.x, uvMin.y), new Vector2(uvMin.x, clipped.y)); AddQuad(toFill, new Vector2(xMax, y1) + rect.position, new Vector2(rect.width, y2) + rect.position, color, new Vector2(uvMax.x, uvMin.y), new Vector2(outer.z, clipped.y)); }
// Bottom and top tiled border clipped = uvMax; for (int i = 0; i < nTilesW; i++) { float x1 = xMin + i * tileWidth; float x2 = xMin + (i + 1) * tileWidth; if (x2 > xMax) { clipped.x = uvMin.x + (uvMax.x - uvMin.x) * (xMax - x1) / (x2 - x1); x2 = xMax; } AddQuad(toFill, new Vector2(x1, 0) + rect.position, new Vector2(x2, yMin) + rect.position, color, new Vector2(uvMin.x, outer.y), new Vector2(clipped.x, uvMin.y)); AddQuad(toFill, new Vector2(x1, yMax) + rect.position, new Vector2(x2, rect.height) + rect.position, color, new Vector2(uvMin.x, uvMax.y), new Vector2(clipped.x, outer.w)); }
// Corners AddQuad(toFill, new Vector2(0, 0) + rect.position, new Vector2(xMin, yMin) + rect.position, color, new Vector2(outer.x, outer.y), new Vector2(uvMin.x, uvMin.y)); AddQuad(toFill, new Vector2(xMax, 0) + rect.position, new Vector2(rect.width, yMin) + rect.position, color, new Vector2(uvMax.x, outer.y), new Vector2(outer.z, uvMin.y)); AddQuad(toFill, new Vector2(0, yMax) + rect.position, new Vector2(xMin, rect.height) + rect.position, color, new Vector2(outer.x, uvMax.y), new Vector2(uvMin.x, outer.w)); AddQuad(toFill, new Vector2(xMax, yMax) + rect.position, new Vector2(rect.width, rect.height) + rect.position, color, new Vector2(uvMax.x, uvMax.y), new Vector2(outer.z, outer.w)); }
// Horizontal and vertical filled sprites are simple -- just end the Image prematurely if (m_FillMethod == FillMethod.Horizontal || m_FillMethod == FillMethod.Vertical) { if (fillMethod == FillMethod.Horizontal) { float fill = (tx1 - tx0) * m_FillAmount;
public virtual bool IsRaycastLocationValid(Vector2 screenPoint, Camera eventCamera) { if (alphaHitTestMinimumThreshold <= 0) return true;
if (alphaHitTestMinimumThreshold > 1) return false;
if (activeSprite == null) return true;
Vector2 local; if (!RectTransformUtility.ScreenPointToLocalPointInRectangle(rectTransform, screenPoint, eventCamera, out local)) return false;
Rect rect = GetPixelAdjustedRect();
// Convert to have lower left corner as reference point. local.x += rectTransform.pivot.x * rect.width; local.y += rectTransform.pivot.y * rect.height;
local = MapCoordinate(local, rect);
// Normalize local coordinates. Rect spriteRect = activeSprite.textureRect; Vector2 normalized = new Vector2(local.x / spriteRect.width, local.y / spriteRect.height);
// Convert to texture space. float x = Mathf.Lerp(spriteRect.x, spriteRect.xMax, normalized.x) / activeSprite.texture.width; float y = Mathf.Lerp(spriteRect.y, spriteRect.yMax, normalized.y) / activeSprite.texture.height;
try { return activeSprite.texture.GetPixelBilinear(x, y).a >= alphaHitTestMinimumThreshold; } catch (UnityException e) { Debug.LogError("Using alphaHitTestMinimumThreshold greater than 0 on Image whose sprite texture cannot be read. " + e.Message + " Also make sure to disable sprite packing for this sprite.", this); return true; } }