size_t DecomposeIntoNoRepeatRects(const gfx::Rect& aRect, const gfx::Rect& aTexCoordRect, decomposedRectArrayT* aLayerRects, decomposedRectArrayT* aTextureRects) { gfx::Rect texCoordRect = aTexCoordRect; // If the texture should be flipped, it will have negative height. Detect that // here and compensate for it. We will flip each rect as we emit it. bool flipped = false; if (texCoordRect.height < 0) { flipped = true; texCoordRect.y += texCoordRect.height; texCoordRect.height = -texCoordRect.height; } // Wrap the texture coordinates so they are within [0,1] and cap width/height // at 1. We rely on this below. texCoordRect = gfx::Rect(gfx::Point(WrapTexCoord(texCoordRect.x), WrapTexCoord(texCoordRect.y)), gfx::Size(std::min(texCoordRect.width, 1.0f), std::min(texCoordRect.height, 1.0f))); NS_ASSERTION(texCoordRect.x >= 0.0f && texCoordRect.x <= 1.0f && texCoordRect.y >= 0.0f && texCoordRect.y <= 1.0f && texCoordRect.width >= 0.0f && texCoordRect.width <= 1.0f && texCoordRect.height >= 0.0f && texCoordRect.height <= 1.0f && texCoordRect.XMost() >= 0.0f && texCoordRect.XMost() <= 2.0f && texCoordRect.YMost() >= 0.0f && texCoordRect.YMost() <= 2.0f, "We just wrapped the texture coordinates, didn't we?"); // Get the top left and bottom right points of the rectangle. Note that // tl.x/tl.y are within [0,1] but br.x/br.y are within [0,2]. gfx::Point tl = texCoordRect.TopLeft(); gfx::Point br = texCoordRect.BottomRight(); NS_ASSERTION(tl.x >= 0.0f && tl.x <= 1.0f && tl.y >= 0.0f && tl.y <= 1.0f && br.x >= tl.x && br.x <= 2.0f && br.y >= tl.y && br.y <= 2.0f && FuzzyLTE(br.x - tl.x, 1.0f) && FuzzyLTE(br.y - tl.y, 1.0f), "Somehow generated invalid texture coordinates"); // Then check if we wrap in either the x or y axis. bool xwrap = br.x > 1.0f; bool ywrap = br.y > 1.0f; // If xwrap is false, the texture will be sampled from tl.x .. br.x. // If xwrap is true, then it will be split into tl.x .. 1.0, and // 0.0 .. WrapTexCoord(br.x). Same for the Y axis. The destination // rectangle is also split appropriately, according to the calculated // xmid/ymid values. if (!xwrap && !ywrap) { SetRects(0, aLayerRects, aTextureRects, aRect.x, aRect.y, aRect.XMost(), aRect.YMost(), tl.x, tl.y, br.x, br.y, flipped); return 1; } // If we are dealing with wrapping br.x and br.y are greater than 1.0 so // wrap them here as well. br = gfx::Point(xwrap ? WrapTexCoord(br.x) : br.x, ywrap ? WrapTexCoord(br.y) : br.y); // If we wrap around along the x axis, we will draw first from // tl.x .. 1.0 and then from 0.0 .. br.x (which we just wrapped above). // The same applies for the Y axis. The midpoints we calculate here are // only valid if we actually wrap around. GLfloat xmid = aRect.x + (1.0f - tl.x) / texCoordRect.width * aRect.width; GLfloat ymid = aRect.y + (1.0f - tl.y) / texCoordRect.height * aRect.height; NS_ASSERTION(!xwrap || (xmid > aRect.x && xmid < aRect.XMost() && FuzzyEqual((xmid - aRect.x) + (aRect.XMost() - xmid), aRect.width)), "xmid should be within [x,XMost()] and the wrapped rect should have the same width"); NS_ASSERTION(!ywrap || (ymid > aRect.y && ymid < aRect.YMost() && FuzzyEqual((ymid - aRect.y) + (aRect.YMost() - ymid), aRect.height)), "ymid should be within [y,YMost()] and the wrapped rect should have the same height"); if (!xwrap && ywrap) { SetRects(0, aLayerRects, aTextureRects, aRect.x, aRect.y, aRect.XMost(), ymid, tl.x, tl.y, br.x, 1.0f, flipped); SetRects(1, aLayerRects, aTextureRects, aRect.x, ymid, aRect.XMost(), aRect.YMost(), tl.x, 0.0f, br.x, br.y, flipped); return 2; } if (xwrap && !ywrap) { SetRects(0, aLayerRects, aTextureRects, aRect.x, aRect.y, xmid, aRect.YMost(), tl.x, tl.y, 1.0f, br.y, flipped); SetRects(1, aLayerRects, aTextureRects, xmid, aRect.y, aRect.XMost(), aRect.YMost(), 0.0f, tl.y, br.x, br.y, flipped); return 2; } SetRects(0, aLayerRects, aTextureRects, aRect.x, aRect.y, xmid, ymid, tl.x, tl.y, 1.0f, 1.0f, flipped); SetRects(1, aLayerRects, aTextureRects, xmid, aRect.y, aRect.XMost(), ymid, 0.0f, tl.y, br.x, 1.0f, flipped); SetRects(2, aLayerRects, aTextureRects, aRect.x, ymid, xmid, aRect.YMost(), tl.x, 0.0f, 1.0f, br.y, flipped); SetRects(3, aLayerRects, aTextureRects, xmid, ymid, aRect.XMost(), aRect.YMost(), 0.0f, 0.0f, br.x, br.y, flipped); return 4; }
void DecomposeIntoNoRepeatTriangles(const gfx::IntRect& aTexCoordRect, const gfx::IntSize& aTexSize, RectTriangles& aRects, bool aFlipY /* = false */) { // normalize this gfx::IntRect tcr(aTexCoordRect); while (tcr.x >= aTexSize.width) tcr.x -= aTexSize.width; while (tcr.y >= aTexSize.height) tcr.y -= aTexSize.height; // Compute top left and bottom right tex coordinates GLfloat tl[2] = { GLfloat(tcr.x) / GLfloat(aTexSize.width), GLfloat(tcr.y) / GLfloat(aTexSize.height) }; GLfloat br[2] = { GLfloat(tcr.XMost()) / GLfloat(aTexSize.width), GLfloat(tcr.YMost()) / GLfloat(aTexSize.height) }; // then check if we wrap in either the x or y axis; if we do, // then also use fmod to figure out the "true" non-wrapping // texture coordinates. bool xwrap = false, ywrap = false; if (tcr.x < 0 || tcr.x > aTexSize.width || tcr.XMost() < 0 || tcr.XMost() > aTexSize.width) { xwrap = true; tl[0] = WrapTexCoord(tl[0]); br[0] = WrapTexCoord(br[0]); } if (tcr.y < 0 || tcr.y > aTexSize.height || tcr.YMost() < 0 || tcr.YMost() > aTexSize.height) { ywrap = true; tl[1] = WrapTexCoord(tl[1]); br[1] = WrapTexCoord(br[1]); } NS_ASSERTION(tl[0] >= 0.0f && tl[0] <= 1.0f && tl[1] >= 0.0f && tl[1] <= 1.0f && br[0] >= 0.0f && br[0] <= 1.0f && br[1] >= 0.0f && br[1] <= 1.0f, "Somehow generated invalid texture coordinates"); // If xwrap is false, the texture will be sampled from tl[0] // .. br[0]. If xwrap is true, then it will be split into tl[0] // .. 1.0, and 0.0 .. br[0]. Same for the Y axis. The // destination rectangle is also split appropriately, according // to the calculated xmid/ymid values. // There isn't a 1:1 mapping between tex coords and destination coords; // when computing midpoints, we have to take that into account. We // need to map the texture coords, which are (in the wrap case): // |tl->1| and |0->br| to the |0->1| range of the vertex coords. So // we have the length (1-tl)+(br) that needs to map into 0->1. // These are only valid if there is wrap involved, they won't be used // otherwise. GLfloat xlen = (1.0f - tl[0]) + br[0]; GLfloat ylen = (1.0f - tl[1]) + br[1]; NS_ASSERTION(!xwrap || xlen > 0.0f, "xlen isn't > 0, what's going on?"); NS_ASSERTION(!ywrap || ylen > 0.0f, "ylen isn't > 0, what's going on?"); NS_ASSERTION(aTexCoordRect.width <= aTexSize.width && aTexCoordRect.height <= aTexSize.height, "tex coord rect would cause tiling!"); if (!xwrap && !ywrap) { aRects.addRect(0.0f, 0.0f, 1.0f, 1.0f, tl[0], tl[1], br[0], br[1], aFlipY); } else if (!xwrap && ywrap) { GLfloat ymid = (1.0f - tl[1]) / ylen; aRects.addRect(0.0f, 0.0f, 1.0f, ymid, tl[0], tl[1], br[0], 1.0f, aFlipY); aRects.addRect(0.0f, ymid, 1.0f, 1.0f, tl[0], 0.0f, br[0], br[1], aFlipY); } else if (xwrap && !ywrap) { GLfloat xmid = (1.0f - tl[0]) / xlen; aRects.addRect(0.0f, 0.0f, xmid, 1.0f, tl[0], tl[1], 1.0f, br[1], aFlipY); aRects.addRect(xmid, 0.0f, 1.0f, 1.0f, 0.0f, tl[1], br[0], br[1], aFlipY); } else { GLfloat xmid = (1.0f - tl[0]) / xlen; GLfloat ymid = (1.0f - tl[1]) / ylen; aRects.addRect(0.0f, 0.0f, xmid, ymid, tl[0], tl[1], 1.0f, 1.0f, aFlipY); aRects.addRect(xmid, 0.0f, 1.0f, ymid, 0.0f, tl[1], br[0], 1.0f, aFlipY); aRects.addRect(0.0f, ymid, xmid, 1.0f, tl[0], 0.0f, 1.0f, br[1], aFlipY); aRects.addRect(xmid, ymid, 1.0f, 1.0f, 0.0f, 0.0f, br[0], br[1], aFlipY); } }