Files
palemoon27/gfx/gl/GLTextureImage.cpp
T
roytam1 3f625686df import changes from `dev' branch of rmottola/Arctic-Fox:
- Bug 1157064 - implementation of font-display. r=heycam,khuey (43fe566f45)
- Bug 1235186 - fix small userfont logging nit. r=m_kato (d40bead913)
- Bug 1188802 - only rebuild local webfont rules when needed. r=heycam (f74200aeb2)
- Backout unrelated code landed in dee3e26cc1c0 by mistake. (5d254b78b6)
- Bug 1236506: Add support for "-webkit-filter" as an alias for CSS property "filter". r=heycam (1e7ac6554a)
- Bug 1230426 - Remove support for -webkit-border-image longhand CSS property aliases. r=dholbert (a1a2d5e82a)
- Bug 1246101 - Restore some auto-completion for the align-/justify-* properties. r=dholbert (a33dd2e7c2)
- Bug 1195142 patch 1 - Set CSS_PROPERTY_CREATES_STACKING_CONTEXT for the opacity property. r=BenWa (e547f7b420)
- Bug 1195142 patch 2 - Add reftests for will-change creating a stacking context. r=BenWa (3bb9dc17b9)
- Bug 1195142 patch 3 - Link to correct specification URLs so the CSSWG test suite system is happy. (a8121cdcf0)
- Bug 1234966 - nsStylePosition::MaxDifference should include nsChangeHint_NeutralChange because CalcDiffrence returns it. r=heycam (aa0bf89e54)
- Bug 1244166: Don't ignore stroke/fill properties in high-contrast mode, since doing so can produce icons that are invisible or whose colors are unrelated to the user's chosen high-contrast colors. r=longsonr (6448b05118)
- Bug 1157057 - Rewrite the handling of the nsITimer object in nrappkitTimerCallback; r=ekr (7cc88409b0)
- Bug 1117984: added proxy connection state enum. r=bwc (0c643ff34a)
- Bug 1231971 - Refactor the NAT simulator to use e10s sockets when appropriate. r=drno (c0722c431b)
- Bug 1231973 - Allow NAT simulator to be enabled with the pref system. r=drno (c92ca4fefa)
- Bug 1201209 - Extend the timeout on socket readiness in test_nr_socket_unittest. r=drno (e9e5400902)
- Bug 1216815 - fix memory leaks in test TCP STUN server. r=mjf (11219f41fc)
- Bug 1194385 - Add new unit tests which demonstrate the current behavior. r=bwc (900c621491)
- crashreporte (2ac99868b6)
- Bug 1150966: Check whether |streams_| is null on stats methods in NrIceMediaStream. r=drno (130a9ac2da)
- Bug 1241690: reduce logging output for unconnected PCs. r=bwc (aa236d7184)
- Bug 1224845 - close sockets on errors and don't connect to IPv4 TURN TCP from IPv6 sockets. r=jesup (f128a67692)
- Bug 1189961 - added DNS AAAA convertion to nICEr transport addr. r=bwc (30c14fe7dd)
- Bug 1247536 - Fix -Wunreachable-code warning in media/mtransport/. r=drno (f6768f8539)
- Bug 1194259: nsresult != NS_IMETHODIMP rs=bustage (3a922e6e14)
- Bug 1237909 part 1 - Remove unused TransportLayer::RunOnThread function. r=bwc (d2d219d63a)
- Bug 1237909 part 2 - Do not return value from task for sync dispatch. r=froydnj (c5ec2aecfc)
- Bug 1245035 - Move LOCAL_INCLUDES to moz.build in media/omx-plugin/lib/ics/libvideoeditorplayer. r=mshal (54c363c9f7)
- Bug 1232069 - Check box sizes before alloc&copy. r=jya (86cfe660e7)
- Bug 1234778: Mark all audio frames as keyframes. r=kentuckyfriedtakahe (5e4f1b54d5)
- Bug 1231169 - report rust mp4parse track status in telemetry. r=kinetik,vladan (260d0fed99)
- Bug 1238420 - Update mp4parse-rust invocations in MP4Metadata to match CAPI changes. r=rillian (64c5d6a1ef)
- Bug 1238420 - Report mp4parse-rust errors via Telemetry. r=rillian,vladan (ff72f8dead)
- Bug 1219452 - Update script for rust mp4parser. r=kinetik (9abc268b60)
- Bug 1220754 - Update rust mp4parse import script for v0.1.3. r=kinetik (7185657598)
- Bug 1224785, Part 1 - Implement alert favicons backend. r=wchen (665c44b0cb)
- Bug 1224785, Part 2 - Show the site favicon in OS X notifications. r=mstange (814ff022ba)
- Bug 1224785, Part 3 - Don't include ShowWith{Icon}Backend on Android. r=me (fe323c2960)
- Bug 1243418 - Fix up incorrect 'aOverwrite' usage and impl in GLUploadHelpers r=jgilbert (67677b4921)
- clarify comment (88003aaf96)
- Bug 1204284: Show paper size options in OS X print dialog. r=smichaud (8bb40b4349)
- Bug 1214511 - Show copies, page range selection, and more on the expanded OSX print dialog. r=mstange (301d5cdccc)
- Bug 1216478 - prefer tooltiptext on a XUL element over title attribute on a containing toolbaritem when determining accessible name, r=surkov (ec1dfcad37)
- Bug 1248838 - ARIA owns change may fail, r=yzen (d183be3f3c)
- Bug 1222531 - turn off -Wextra-tokens on clang-cl in accessible/ directories; r=tbsaunde (6dd4dcae20)
- bug 1241453 - add DocAccessibleParent::GetXPCAccessible() r=davidb (f243398399)
- Bug 1087608 - eliminating a pref observer leak and fixing test timeout overflow that cause intermittents. r=eeejay (413354c349)
- Bug 1238368 - Re-introduce workaround for Android tap gesture. r=yzen (04bb9cea5a)
- Bug 1233863 - ARM64: Disable tests that require ion.enable = 1. r=jimb (b268c03c22)
- Bug 1191976 - Intentionally crash if we hit an IPC FatalError in the  parent process. r=billm (b6e9d90d34)
- Bug 1194721: Add |DaemonRunnable8|, r=shuang (0b293cb8a5)
- Bug 1194721: Add PDU_ prefix to daemon PDU constants, r=shuang (834240b14b)
- Bug 1228546 - Implement peripheral mode support for GATT API. r=brsun, r=mrbkap (01a711cac6)
- Bug 1194721: Add helpers for Gonk sensors daemon, r=gsvelto (524d1d6792)
2024-01-02 22:59:35 +08:00

757 lines
24 KiB
C++

/* -*- Mode: c++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40; -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "GLTextureImage.h"
#include "GLContext.h"
#include "gfxContext.h"
#include "gfxPlatform.h"
#include "gfxUtils.h"
#include "gfx2DGlue.h"
#include "mozilla/gfx/2D.h"
#include "ScopedGLHelpers.h"
#include "GLUploadHelpers.h"
#include "GfxTexturesReporter.h"
#include "TextureImageEGL.h"
#ifdef XP_MACOSX
#include "TextureImageCGL.h"
#endif
using namespace mozilla::gfx;
namespace mozilla {
namespace gl {
already_AddRefed<TextureImage>
CreateTextureImage(GLContext* gl,
const gfx::IntSize& aSize,
TextureImage::ContentType aContentType,
GLenum aWrapMode,
TextureImage::Flags aFlags,
TextureImage::ImageFormat aImageFormat)
{
switch (gl->GetContextType()) {
#ifdef XP_MACOSX
case GLContextType::CGL:
return CreateTextureImageCGL(gl, aSize, aContentType, aWrapMode, aFlags, aImageFormat);
#endif
case GLContextType::EGL:
return CreateTextureImageEGL(gl, aSize, aContentType, aWrapMode, aFlags, aImageFormat);
default:
return CreateBasicTextureImage(gl, aSize, aContentType, aWrapMode, aFlags);
}
}
static already_AddRefed<TextureImage>
TileGenFunc(GLContext* gl,
const IntSize& aSize,
TextureImage::ContentType aContentType,
TextureImage::Flags aFlags,
TextureImage::ImageFormat aImageFormat)
{
switch (gl->GetContextType()) {
#ifdef XP_MACOSX
case GLContextType::CGL:
return TileGenFuncCGL(gl, aSize, aContentType, aFlags);
#endif
case GLContextType::EGL:
return TileGenFuncEGL(gl, aSize, aContentType, aFlags, aImageFormat);
default:
return nullptr;
}
}
already_AddRefed<TextureImage>
TextureImage::Create(GLContext* gl,
const gfx::IntSize& size,
TextureImage::ContentType contentType,
GLenum wrapMode,
TextureImage::Flags flags)
{
return CreateTextureImage(gl, size, contentType, wrapMode, flags);
}
bool
TextureImage::UpdateFromDataSource(gfx::DataSourceSurface *aSurface,
const nsIntRegion* aDestRegion,
const gfx::IntPoint* aSrcPoint)
{
nsIntRegion destRegion = aDestRegion ? *aDestRegion
: IntRect(0, 0,
aSurface->GetSize().width,
aSurface->GetSize().height);
gfx::IntPoint srcPoint = aSrcPoint ? *aSrcPoint
: gfx::IntPoint(0, 0);
return DirectUpdate(aSurface, destRegion, srcPoint);
}
gfx::IntRect TextureImage::GetTileRect() {
return gfx::IntRect(gfx::IntPoint(0,0), mSize);
}
gfx::IntRect TextureImage::GetSrcTileRect() {
return GetTileRect();
}
void
TextureImage::UpdateUploadSize(size_t amount)
{
if (mUploadSize > 0) {
GfxTexturesReporter::UpdateAmount(GfxTexturesReporter::MemoryFreed, mUploadSize);
}
mUploadSize = amount;
GfxTexturesReporter::UpdateAmount(GfxTexturesReporter::MemoryAllocated, mUploadSize);
}
BasicTextureImage::~BasicTextureImage()
{
GLContext *ctx = mGLContext;
if (ctx->IsDestroyed() || !ctx->IsOwningThreadCurrent()) {
ctx = ctx->GetSharedContext();
}
// If we have a context, then we need to delete the texture;
// if we don't have a context (either real or shared),
// then they went away when the contex was deleted, because it
// was the only one that had access to it.
if (ctx && ctx->MakeCurrent()) {
ctx->fDeleteTextures(1, &mTexture);
}
}
gfx::DrawTarget*
BasicTextureImage::BeginUpdate(nsIntRegion& aRegion)
{
NS_ASSERTION(!mUpdateDrawTarget, "BeginUpdate() without EndUpdate()?");
// determine the region the client will need to repaint
if (CanUploadSubTextures(mGLContext)) {
GetUpdateRegion(aRegion);
} else {
aRegion = IntRect(IntPoint(0, 0), mSize);
}
mUpdateRegion = aRegion;
IntRect rgnSize = mUpdateRegion.GetBounds();
if (!IntRect(IntPoint(0, 0), mSize).Contains(rgnSize)) {
NS_ERROR("update outside of image");
return nullptr;
}
gfx::SurfaceFormat format =
(GetContentType() == gfxContentType::COLOR) ?
gfx::SurfaceFormat::B8G8R8X8 : gfx::SurfaceFormat::B8G8R8A8;
mUpdateDrawTarget =
GetDrawTargetForUpdate(gfx::IntSize(rgnSize.width, rgnSize.height), format);
return mUpdateDrawTarget;
}
void
BasicTextureImage::GetUpdateRegion(nsIntRegion& aForRegion)
{
// if the texture hasn't been initialized yet, or something important
// changed, we need to recreate our backing surface and force the
// client to paint everything
if (mTextureState != Valid) {
aForRegion = IntRect(IntPoint(0, 0), mSize);
}
}
void
BasicTextureImage::EndUpdate()
{
NS_ASSERTION(!!mUpdateDrawTarget, "EndUpdate() without BeginUpdate()?");
// FIXME: this is the slow boat. Make me fast (with GLXPixmap?).
RefPtr<gfx::SourceSurface> updateSnapshot = mUpdateDrawTarget->Snapshot();
RefPtr<gfx::DataSourceSurface> updateData = updateSnapshot->GetDataSurface();
bool relative = FinishedSurfaceUpdate();
bool needInit = mTextureState == Created;
size_t uploadSize;
mTextureFormat =
UploadSurfaceToTexture(mGLContext,
updateData,
mUpdateRegion,
mTexture,
&uploadSize,
needInit,
mUpdateOffset,
relative);
FinishedSurfaceUpload();
if (uploadSize > 0) {
UpdateUploadSize(uploadSize);
}
mUpdateDrawTarget = nullptr;
mTextureState = Valid;
}
void
BasicTextureImage::BindTexture(GLenum aTextureUnit)
{
mGLContext->fActiveTexture(aTextureUnit);
mGLContext->fBindTexture(LOCAL_GL_TEXTURE_2D, mTexture);
mGLContext->fActiveTexture(LOCAL_GL_TEXTURE0);
}
already_AddRefed<gfx::DrawTarget>
BasicTextureImage::GetDrawTargetForUpdate(const gfx::IntSize& aSize, gfx::SurfaceFormat aFmt)
{
return gfx::Factory::CreateDrawTarget(gfx::BackendType::CAIRO, aSize, aFmt);
}
bool
BasicTextureImage::FinishedSurfaceUpdate()
{
return false;
}
void
BasicTextureImage::FinishedSurfaceUpload()
{
}
bool
BasicTextureImage::DirectUpdate(gfx::DataSourceSurface* aSurf, const nsIntRegion& aRegion, const gfx::IntPoint& aFrom /* = gfx::IntPoint(0, 0) */)
{
IntRect bounds = aRegion.GetBounds();
nsIntRegion region;
if (mTextureState != Valid) {
bounds = IntRect(0, 0, mSize.width, mSize.height);
region = nsIntRegion(bounds);
} else {
region = aRegion;
}
size_t uploadSize;
bool needInit = mTextureState == Created;
mTextureFormat =
UploadSurfaceToTexture(mGLContext,
aSurf,
region,
mTexture,
&uploadSize,
needInit,
bounds.TopLeft() + IntPoint(aFrom.x, aFrom.y),
false);
if (uploadSize > 0) {
UpdateUploadSize(uploadSize);
}
mTextureState = Valid;
return true;
}
void
BasicTextureImage::Resize(const gfx::IntSize& aSize)
{
NS_ASSERTION(!mUpdateDrawTarget, "Resize() while in update?");
mGLContext->fBindTexture(LOCAL_GL_TEXTURE_2D, mTexture);
// This matches the logic in UploadImageDataToTexture so that
// we avoid mixing formats.
GLenum format;
GLenum type;
if (mGLContext->GetPreferredARGB32Format() == LOCAL_GL_BGRA) {
MOZ_ASSERT(!mGLContext->IsGLES());
format = LOCAL_GL_BGRA;
type = LOCAL_GL_UNSIGNED_INT_8_8_8_8_REV;
} else {
format = LOCAL_GL_RGBA;
type = LOCAL_GL_UNSIGNED_BYTE;
}
mGLContext->fTexImage2D(LOCAL_GL_TEXTURE_2D,
0,
LOCAL_GL_RGBA,
aSize.width,
aSize.height,
0,
format,
type,
nullptr);
mTextureState = Allocated;
mSize = aSize;
}
gfx::IntSize TextureImage::GetSize() const {
return mSize;
}
TextureImage::TextureImage(const gfx::IntSize& aSize,
GLenum aWrapMode, ContentType aContentType,
Flags aFlags)
: mSize(aSize)
, mWrapMode(aWrapMode)
, mContentType(aContentType)
, mTextureFormat(gfx::SurfaceFormat::UNKNOWN)
, mFilter(Filter::GOOD)
, mFlags(aFlags)
, mUploadSize(0)
{}
BasicTextureImage::BasicTextureImage(GLuint aTexture,
const gfx::IntSize& aSize,
GLenum aWrapMode,
ContentType aContentType,
GLContext* aContext,
TextureImage::Flags aFlags)
: TextureImage(aSize, aWrapMode, aContentType, aFlags)
, mTexture(aTexture)
, mTextureState(Created)
, mGLContext(aContext)
, mUpdateOffset(0, 0)
{}
static bool
WantsSmallTiles(GLContext* gl)
{
// We can't use small tiles on the SGX 540, because of races in texture upload.
if (gl->WorkAroundDriverBugs() &&
gl->Renderer() == GLRenderer::SGX540)
return false;
// We should use small tiles for good performance if we can't use
// glTexSubImage2D() for some reason.
if (!CanUploadSubTextures(gl))
return true;
// Don't use small tiles otherwise. (If we implement incremental texture upload,
// then we will want to revisit this.)
return false;
}
TiledTextureImage::TiledTextureImage(GLContext* aGL,
gfx::IntSize aSize,
TextureImage::ContentType aContentType,
TextureImage::Flags aFlags,
TextureImage::ImageFormat aImageFormat)
: TextureImage(aSize, LOCAL_GL_CLAMP_TO_EDGE, aContentType, aFlags)
, mCurrentImage(0)
, mIterationCallback(nullptr)
, mIterationCallbackData(nullptr)
, mInUpdate(false)
, mRows(0)
, mColumns(0)
, mGL(aGL)
, mTextureState(Created)
, mImageFormat(aImageFormat)
{
if (!(aFlags & TextureImage::DisallowBigImage) && WantsSmallTiles(mGL)) {
mTileSize = 256;
} else {
mGL->fGetIntegerv(LOCAL_GL_MAX_TEXTURE_SIZE, (GLint*) &mTileSize);
}
if (aSize.width != 0 && aSize.height != 0) {
Resize(aSize);
}
}
TiledTextureImage::~TiledTextureImage()
{
}
bool
TiledTextureImage::DirectUpdate(gfx::DataSourceSurface* aSurf, const nsIntRegion& aRegion, const gfx::IntPoint& aFrom /* = gfx::IntPoint(0, 0) */)
{
if (mSize.width == 0 || mSize.height == 0) {
return true;
}
nsIntRegion region;
if (mTextureState != Valid) {
IntRect bounds = IntRect(0, 0, mSize.width, mSize.height);
region = nsIntRegion(bounds);
} else {
region = aRegion;
}
bool result = true;
int oldCurrentImage = mCurrentImage;
BeginBigImageIteration();
do {
IntRect tileRect = GetSrcTileRect();
int xPos = tileRect.x;
int yPos = tileRect.y;
nsIntRegion tileRegion;
tileRegion.And(region, tileRect); // intersect with tile
if (tileRegion.IsEmpty())
continue;
if (CanUploadSubTextures(mGL)) {
tileRegion.MoveBy(-xPos, -yPos); // translate into tile local space
} else {
// If sub-textures are unsupported, expand to tile boundaries
tileRect.x = tileRect.y = 0;
tileRegion = nsIntRegion(tileRect);
}
result &= mImages[mCurrentImage]->
DirectUpdate(aSurf, tileRegion, aFrom + gfx::IntPoint(xPos, yPos));
if (mCurrentImage == mImages.Length() - 1) {
// We know we're done, but we still need to ensure that the callback
// gets called (e.g. to update the uploaded region).
NextTile();
break;
}
// Override a callback cancelling iteration if the texture wasn't valid.
// We need to force the update in that situation, or we may end up
// showing invalid/out-of-date texture data.
} while (NextTile() || (mTextureState != Valid));
mCurrentImage = oldCurrentImage;
mTextureFormat = mImages[0]->GetTextureFormat();
mTextureState = Valid;
return result;
}
void
TiledTextureImage::GetUpdateRegion(nsIntRegion& aForRegion)
{
if (mTextureState != Valid) {
// if the texture hasn't been initialized yet, or something important
// changed, we need to recreate our backing surface and force the
// client to paint everything
aForRegion = IntRect(IntPoint(0, 0), mSize);
return;
}
nsIntRegion newRegion;
// We need to query each texture with the region it will be drawing and
// set aForRegion to be the combination of all of these regions
for (unsigned i = 0; i < mImages.Length(); i++) {
int xPos = (i % mColumns) * mTileSize;
int yPos = (i / mColumns) * mTileSize;
IntRect imageRect = IntRect(IntPoint(xPos,yPos),
mImages[i]->GetSize());
if (aForRegion.Intersects(imageRect)) {
// Make a copy of the region
nsIntRegion subRegion;
subRegion.And(aForRegion, imageRect);
// Translate it into tile-space
subRegion.MoveBy(-xPos, -yPos);
// Query region
mImages[i]->GetUpdateRegion(subRegion);
// Translate back
subRegion.MoveBy(xPos, yPos);
// Add to the accumulated region
newRegion.Or(newRegion, subRegion);
}
}
aForRegion = newRegion;
}
gfx::DrawTarget*
TiledTextureImage::BeginUpdate(nsIntRegion& aRegion)
{
NS_ASSERTION(!mInUpdate, "nested update");
mInUpdate = true;
// Note, we don't call GetUpdateRegion here as if the updated region is
// fully contained in a single tile, we get to avoid iterating through
// the tiles again (and a little copying).
if (mTextureState != Valid)
{
// if the texture hasn't been initialized yet, or something important
// changed, we need to recreate our backing surface and force the
// client to paint everything
aRegion = IntRect(IntPoint(0, 0), mSize);
}
IntRect bounds = aRegion.GetBounds();
for (unsigned i = 0; i < mImages.Length(); i++) {
int xPos = (i % mColumns) * mTileSize;
int yPos = (i / mColumns) * mTileSize;
nsIntRegion imageRegion =
nsIntRegion(IntRect(IntPoint(xPos,yPos),
mImages[i]->GetSize()));
// a single Image can handle this update request
if (imageRegion.Contains(aRegion)) {
// adjust for tile offset
aRegion.MoveBy(-xPos, -yPos);
// forward the actual call
RefPtr<gfx::DrawTarget> drawTarget = mImages[i]->BeginUpdate(aRegion);
// caller expects container space
aRegion.MoveBy(xPos, yPos);
// we don't have a temp surface
mUpdateDrawTarget = nullptr;
// remember which image to EndUpdate
mCurrentImage = i;
return drawTarget.get();
}
}
// Get the real updated region, taking into account the capabilities of
// each TextureImage tile
GetUpdateRegion(aRegion);
mUpdateRegion = aRegion;
bounds = aRegion.GetBounds();
// update covers multiple Images - create a temp surface to paint in
gfx::SurfaceFormat format =
(GetContentType() == gfxContentType::COLOR) ?
gfx::SurfaceFormat::B8G8R8X8: gfx::SurfaceFormat::B8G8R8A8;
mUpdateDrawTarget = gfx::Factory::CreateDrawTarget(gfx::BackendType::CAIRO,
bounds.Size(),
format);
return mUpdateDrawTarget;;
}
void
TiledTextureImage::EndUpdate()
{
NS_ASSERTION(mInUpdate, "EndUpdate not in update");
if (!mUpdateDrawTarget) { // update was to a single TextureImage
mImages[mCurrentImage]->EndUpdate();
mInUpdate = false;
mTextureState = Valid;
mTextureFormat = mImages[mCurrentImage]->GetTextureFormat();
return;
}
RefPtr<gfx::SourceSurface> updateSnapshot = mUpdateDrawTarget->Snapshot();
RefPtr<gfx::DataSourceSurface> updateData = updateSnapshot->GetDataSurface();
// upload tiles from temp surface
for (unsigned i = 0; i < mImages.Length(); i++) {
int xPos = (i % mColumns) * mTileSize;
int yPos = (i / mColumns) * mTileSize;
IntRect imageRect = IntRect(IntPoint(xPos,yPos), mImages[i]->GetSize());
nsIntRegion subregion;
subregion.And(mUpdateRegion, imageRect);
if (subregion.IsEmpty())
continue;
subregion.MoveBy(-xPos, -yPos); // Tile-local space
// copy tile from temp target
gfx::DrawTarget* drawTarget = mImages[i]->BeginUpdate(subregion);
MOZ_ASSERT(drawTarget->GetBackendType() == BackendType::CAIRO,
"updateSnapshot should not have been converted to data");
gfxUtils::ClipToRegion(drawTarget, subregion);
Size size(updateData->GetSize().width,
updateData->GetSize().height);
drawTarget->DrawSurface(updateData,
Rect(Point(-xPos, -yPos), size),
Rect(Point(0, 0), size),
DrawSurfaceOptions(),
DrawOptions(1.0, CompositionOp::OP_SOURCE,
AntialiasMode::NONE));
drawTarget->PopClip();
mImages[i]->EndUpdate();
}
mUpdateDrawTarget = nullptr;
mInUpdate = false;
mTextureFormat = mImages[0]->GetTextureFormat();
mTextureState = Valid;
}
void TiledTextureImage::BeginBigImageIteration()
{
mCurrentImage = 0;
}
bool TiledTextureImage::NextTile()
{
bool continueIteration = true;
if (mIterationCallback)
continueIteration = mIterationCallback(this, mCurrentImage,
mIterationCallbackData);
if (mCurrentImage + 1 < mImages.Length()) {
mCurrentImage++;
return continueIteration;
}
return false;
}
void TiledTextureImage::SetIterationCallback(BigImageIterationCallback aCallback,
void* aCallbackData)
{
mIterationCallback = aCallback;
mIterationCallbackData = aCallbackData;
}
gfx::IntRect TiledTextureImage::GetTileRect()
{
if (!GetTileCount()) {
return gfx::IntRect();
}
gfx::IntRect rect = mImages[mCurrentImage]->GetTileRect();
unsigned int xPos = (mCurrentImage % mColumns) * mTileSize;
unsigned int yPos = (mCurrentImage / mColumns) * mTileSize;
rect.MoveBy(xPos, yPos);
return rect;
}
gfx::IntRect TiledTextureImage::GetSrcTileRect()
{
gfx::IntRect rect = GetTileRect();
const bool needsYFlip = mFlags & OriginBottomLeft;
unsigned int srcY = needsYFlip ? mSize.height - rect.height - rect.y
: rect.y;
return gfx::IntRect(rect.x, srcY, rect.width, rect.height);
}
void
TiledTextureImage::BindTexture(GLenum aTextureUnit)
{
if (!GetTileCount()) {
return;
}
mImages[mCurrentImage]->BindTexture(aTextureUnit);
}
/*
* Resize, trying to reuse tiles. The reuse strategy is to decide on reuse per
* column. A tile on a column is reused if it hasn't changed size, otherwise it
* is discarded/replaced. Extra tiles on a column are pruned after iterating
* each column, and extra rows are pruned after iteration over the entire image
* finishes.
*/
void TiledTextureImage::Resize(const gfx::IntSize& aSize)
{
if (mSize == aSize && mTextureState != Created) {
return;
}
// calculate rows and columns, rounding up
unsigned int columns = (aSize.width + mTileSize - 1) / mTileSize;
unsigned int rows = (aSize.height + mTileSize - 1) / mTileSize;
// Iterate over old tile-store and insert/remove tiles as necessary
int row;
unsigned int i = 0;
for (row = 0; row < (int)rows; row++) {
// If we've gone beyond how many rows there were before, set mColumns to
// zero so that we only create new tiles.
if (row >= (int)mRows)
mColumns = 0;
// Similarly, if we're on the last row of old tiles and the height has
// changed, discard all tiles in that row.
// This will cause the pruning of columns not to work, but we don't need
// to worry about that, as no more tiles will be reused past this point
// anyway.
if ((row == (int)mRows - 1) && (aSize.height != mSize.height))
mColumns = 0;
int col;
for (col = 0; col < (int)columns; col++) {
IntSize size( // use tilesize first, then the remainder
(col+1) * mTileSize > (unsigned int)aSize.width ? aSize.width % mTileSize : mTileSize,
(row+1) * mTileSize > (unsigned int)aSize.height ? aSize.height % mTileSize : mTileSize);
bool replace = false;
// Check if we can re-use old tiles.
if (col < (int)mColumns) {
// Reuse an existing tile. If the tile is an end-tile and the
// width differs, replace it instead.
if (mSize.width != aSize.width) {
if (col == (int)mColumns - 1) {
// Tile at the end of the old column, replace it with
// a new one.
replace = true;
} else if (col == (int)columns - 1) {
// Tile at the end of the new column, create a new one.
} else {
// Before the last column on both the old and new sizes,
// reuse existing tile.
i++;
continue;
}
} else {
// Width hasn't changed, reuse existing tile.
i++;
continue;
}
}
// Create a new tile.
RefPtr<TextureImage> teximg =
TileGenFunc(mGL, size, mContentType, mFlags, mImageFormat);
if (replace)
mImages.ReplaceElementAt(i, teximg);
else
mImages.InsertElementAt(i, teximg);
i++;
}
// Prune any unused tiles on the end of the column.
if (row < (int)mRows) {
for (col = (int)mColumns - col; col > 0; col--) {
mImages.RemoveElementAt(i);
}
}
}
// Prune any unused tiles at the end of the store.
unsigned int length = mImages.Length();
for (; i < length; i++)
mImages.RemoveElementAt(mImages.Length()-1);
// Reset tile-store properties.
mRows = rows;
mColumns = columns;
mSize = aSize;
mTextureState = Allocated;
mCurrentImage = 0;
}
uint32_t TiledTextureImage::GetTileCount()
{
return mImages.Length();
}
already_AddRefed<TextureImage>
CreateBasicTextureImage(GLContext* aGL,
const gfx::IntSize& aSize,
TextureImage::ContentType aContentType,
GLenum aWrapMode,
TextureImage::Flags aFlags)
{
bool useNearestFilter = aFlags & TextureImage::UseNearestFilter;
if (!aGL->MakeCurrent()) {
return nullptr;
}
GLuint texture = 0;
aGL->fGenTextures(1, &texture);
ScopedBindTexture bind(aGL, texture);
GLint texfilter = useNearestFilter ? LOCAL_GL_NEAREST : LOCAL_GL_LINEAR;
aGL->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_MIN_FILTER, texfilter);
aGL->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_MAG_FILTER, texfilter);
aGL->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_WRAP_S, aWrapMode);
aGL->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_WRAP_T, aWrapMode);
RefPtr<BasicTextureImage> texImage =
new BasicTextureImage(texture, aSize, aWrapMode, aContentType,
aGL, aFlags);
return texImage.forget();
}
} // namespace gl
} // namespace mozilla