1 /* -*- Mode: c++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40; -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "GLTextureImage.h"
8 #include "gfxContext.h"
9 #include "gfxPlatform.h"
11 #include "gfx2DGlue.h"
16 already_AddRefed
<TextureImage
>
17 TextureImage::Create(GLContext
* gl
,
18 const nsIntSize
& size
,
19 TextureImage::ContentType contentType
,
21 TextureImage::Flags flags
)
23 return gl
->CreateTextureImage(size
, contentType
, wrapMode
, flags
);
27 TextureImage::UpdateFromDataSource(gfx::DataSourceSurface
*aSurface
,
28 const nsIntRegion
* aDestRegion
,
29 const gfx::IntPoint
* aSrcPoint
)
31 nsIntRegion destRegion
= aDestRegion
? *aDestRegion
33 aSurface
->GetSize().width
,
34 aSurface
->GetSize().height
);
35 nsIntPoint thebesSrcPoint
= aSrcPoint
? nsIntPoint(aSrcPoint
->x
, aSrcPoint
->y
)
37 RefPtr
<gfxASurface
> thebesSurf
38 = new gfxImageSurface(aSurface
->GetData(),
39 ThebesIntSize(aSurface
->GetSize()),
41 SurfaceFormatToImageFormat(aSurface
->GetFormat()));
42 return DirectUpdate(thebesSurf
, destRegion
, thebesSrcPoint
);
45 BasicTextureImage::~BasicTextureImage()
47 GLContext
*ctx
= mGLContext
;
48 if (ctx
->IsDestroyed() || !ctx
->IsOwningThreadCurrent()) {
49 ctx
= ctx
->GetSharedContext();
52 // If we have a context, then we need to delete the texture;
53 // if we don't have a context (either real or shared),
54 // then they went away when the contex was deleted, because it
55 // was the only one that had access to it.
56 if (ctx
&& !ctx
->IsDestroyed()) {
57 mGLContext
->MakeCurrent();
58 mGLContext
->fDeleteTextures(1, &mTexture
);
63 BasicTextureImage::BeginUpdate(nsIntRegion
& aRegion
)
65 NS_ASSERTION(!mUpdateSurface
, "BeginUpdate() without EndUpdate()?");
67 // determine the region the client will need to repaint
68 if (mGLContext
->CanUploadSubTextures()) {
69 GetUpdateRegion(aRegion
);
71 aRegion
= nsIntRect(nsIntPoint(0, 0), mSize
);
74 mUpdateRegion
= aRegion
;
76 nsIntRect rgnSize
= mUpdateRegion
.GetBounds();
77 if (!nsIntRect(nsIntPoint(0, 0), mSize
).Contains(rgnSize
)) {
78 NS_ERROR("update outside of image");
83 (GetContentType() == gfxASurface::CONTENT_COLOR
) ?
84 gfxASurface::ImageFormatRGB24
: gfxASurface::ImageFormatARGB32
;
86 GetSurfaceForUpdate(gfxIntSize(rgnSize
.width
, rgnSize
.height
), format
);
88 if (!mUpdateSurface
|| mUpdateSurface
->CairoStatus()) {
89 mUpdateSurface
= nullptr;
93 mUpdateSurface
->SetDeviceOffset(gfxPoint(-rgnSize
.x
, -rgnSize
.y
));
95 return mUpdateSurface
;
99 BasicTextureImage::GetUpdateRegion(nsIntRegion
& aForRegion
)
101 // if the texture hasn't been initialized yet, or something important
102 // changed, we need to recreate our backing surface and force the
103 // client to paint everything
104 if (mTextureState
!= Valid
)
105 aForRegion
= nsIntRect(nsIntPoint(0, 0), mSize
);
109 BasicTextureImage::EndUpdate()
111 NS_ASSERTION(!!mUpdateSurface
, "EndUpdate() without BeginUpdate()?");
113 // FIXME: this is the slow boat. Make me fast (with GLXPixmap?).
115 // Undo the device offset that BeginUpdate set; doesn't much matter for us here,
116 // but important if we ever do anything directly with the surface.
117 mUpdateSurface
->SetDeviceOffset(gfxPoint(0, 0));
119 bool relative
= FinishedSurfaceUpdate();
122 mGLContext
->UploadSurfaceToTexture(mUpdateSurface
,
125 mTextureState
== Created
,
128 FinishedSurfaceUpload();
130 mUpdateSurface
= nullptr;
131 mTextureState
= Valid
;
135 BasicTextureImage::BindTexture(GLenum aTextureUnit
)
137 mGLContext
->fActiveTexture(aTextureUnit
);
138 mGLContext
->fBindTexture(LOCAL_GL_TEXTURE_2D
, mTexture
);
139 mGLContext
->fActiveTexture(LOCAL_GL_TEXTURE0
);
143 BasicTextureImage::ApplyFilter()
145 mGLContext
->ApplyFilterToBoundTexture(mFilter
);
149 already_AddRefed
<gfxASurface
>
150 BasicTextureImage::GetSurfaceForUpdate(const gfxIntSize
& aSize
, ImageFormat aFmt
)
152 return gfxPlatform::GetPlatform()->
153 CreateOffscreenSurface(aSize
, gfxASurface::ContentFromFormat(aFmt
));
157 BasicTextureImage::FinishedSurfaceUpdate()
163 BasicTextureImage::FinishedSurfaceUpload()
168 BasicTextureImage::DirectUpdate(gfxASurface
* aSurf
, const nsIntRegion
& aRegion
, const nsIntPoint
& aFrom
/* = nsIntPoint(0, 0) */)
170 nsIntRect bounds
= aRegion
.GetBounds();
172 if (mTextureState
!= Valid
) {
173 bounds
= nsIntRect(0, 0, mSize
.width
, mSize
.height
);
174 region
= nsIntRegion(bounds
);
180 mGLContext
->UploadSurfaceToTexture(aSurf
,
183 mTextureState
== Created
,
184 bounds
.TopLeft() + aFrom
,
186 mTextureState
= Valid
;
191 BasicTextureImage::Resize(const nsIntSize
& aSize
)
193 NS_ASSERTION(!mUpdateSurface
, "Resize() while in update?");
195 mGLContext
->fBindTexture(LOCAL_GL_TEXTURE_2D
, mTexture
);
197 mGLContext
->fTexImage2D(LOCAL_GL_TEXTURE_2D
,
204 LOCAL_GL_UNSIGNED_BYTE
,
207 mTextureState
= Allocated
;
211 TiledTextureImage::TiledTextureImage(GLContext
* aGL
,
213 TextureImage::ContentType aContentType
,
214 TextureImage::Flags aFlags
)
215 : TextureImage(aSize
, LOCAL_GL_CLAMP_TO_EDGE
, aContentType
, aFlags
)
217 , mIterationCallback(nullptr)
222 , mTextureState(Created
)
224 if (!(aFlags
& TextureImage::ForceSingleTile
) && mGL
->WantsSmallTiles()) {
227 mGL
->fGetIntegerv(LOCAL_GL_MAX_TEXTURE_SIZE
, (GLint
*) &mTileSize
);
229 if (aSize
.width
!= 0 && aSize
.height
!= 0) {
234 TiledTextureImage::~TiledTextureImage()
239 TiledTextureImage::DirectUpdate(gfxASurface
* aSurf
, const nsIntRegion
& aRegion
, const nsIntPoint
& aFrom
/* = nsIntPoint(0, 0) */)
241 if (mSize
.width
== 0 || mSize
.height
== 0) {
247 if (mTextureState
!= Valid
) {
248 nsIntRect bounds
= nsIntRect(0, 0, mSize
.width
, mSize
.height
);
249 region
= nsIntRegion(bounds
);
255 int oldCurrentImage
= mCurrentImage
;
256 BeginTileIteration();
258 nsIntRect tileRect
= GetSrcTileRect();
259 int xPos
= tileRect
.x
;
260 int yPos
= tileRect
.y
;
262 nsIntRegion tileRegion
;
263 tileRegion
.And(region
, tileRect
); // intersect with tile
265 if (tileRegion
.IsEmpty())
268 if (mGL
->CanUploadSubTextures()) {
269 tileRegion
.MoveBy(-xPos
, -yPos
); // translate into tile local space
271 // If sub-textures are unsupported, expand to tile boundaries
272 tileRect
.x
= tileRect
.y
= 0;
273 tileRegion
= nsIntRegion(tileRect
);
276 result
&= mImages
[mCurrentImage
]->
277 DirectUpdate(aSurf
, tileRegion
, aFrom
+ nsIntPoint(xPos
, yPos
));
279 if (mCurrentImage
== mImages
.Length() - 1) {
280 // We know we're done, but we still need to ensure that the callback
281 // gets called (e.g. to update the uploaded region).
285 // Override a callback cancelling iteration if the texture wasn't valid.
286 // We need to force the update in that situation, or we may end up
287 // showing invalid/out-of-date texture data.
288 } while (NextTile() || (mTextureState
!= Valid
));
289 mCurrentImage
= oldCurrentImage
;
291 mTextureFormat
= mImages
[0]->GetTextureFormat();
292 mTextureState
= Valid
;
297 TiledTextureImage::GetUpdateRegion(nsIntRegion
& aForRegion
)
299 if (mTextureState
!= Valid
) {
300 // if the texture hasn't been initialized yet, or something important
301 // changed, we need to recreate our backing surface and force the
302 // client to paint everything
303 aForRegion
= nsIntRect(nsIntPoint(0, 0), mSize
);
307 nsIntRegion newRegion
;
309 // We need to query each texture with the region it will be drawing and
310 // set aForRegion to be the combination of all of these regions
311 for (unsigned i
= 0; i
< mImages
.Length(); i
++) {
312 int xPos
= (i
% mColumns
) * mTileSize
;
313 int yPos
= (i
/ mColumns
) * mTileSize
;
314 nsIntRect imageRect
= nsIntRect(nsIntRect(nsIntPoint(xPos
,yPos
), mImages
[i
]->GetSize()));
316 if (aForRegion
.Intersects(imageRect
)) {
317 // Make a copy of the region
318 nsIntRegion subRegion
;
319 subRegion
.And(aForRegion
, imageRect
);
320 // Translate it into tile-space
321 subRegion
.MoveBy(-xPos
, -yPos
);
323 mImages
[i
]->GetUpdateRegion(subRegion
);
325 subRegion
.MoveBy(xPos
, yPos
);
326 // Add to the accumulated region
327 newRegion
.Or(newRegion
, subRegion
);
331 aForRegion
= newRegion
;
335 TiledTextureImage::BeginUpdate(nsIntRegion
& aRegion
)
337 NS_ASSERTION(!mInUpdate
, "nested update");
340 // Note, we don't call GetUpdateRegion here as if the updated region is
341 // fully contained in a single tile, we get to avoid iterating through
342 // the tiles again (and a little copying).
343 if (mTextureState
!= Valid
)
345 // if the texture hasn't been initialized yet, or something important
346 // changed, we need to recreate our backing surface and force the
347 // client to paint everything
348 aRegion
= nsIntRect(nsIntPoint(0, 0), mSize
);
351 nsIntRect bounds
= aRegion
.GetBounds();
353 for (unsigned i
= 0; i
< mImages
.Length(); i
++) {
354 int xPos
= (i
% mColumns
) * mTileSize
;
355 int yPos
= (i
/ mColumns
) * mTileSize
;
356 nsIntRegion imageRegion
= nsIntRegion(nsIntRect(nsIntPoint(xPos
,yPos
), mImages
[i
]->GetSize()));
358 // a single Image can handle this update request
359 if (imageRegion
.Contains(aRegion
)) {
360 // adjust for tile offset
361 aRegion
.MoveBy(-xPos
, -yPos
);
362 // forward the actual call
363 nsRefPtr
<gfxASurface
> surface
= mImages
[i
]->BeginUpdate(aRegion
);
364 // caller expects container space
365 aRegion
.MoveBy(xPos
, yPos
);
366 // Correct the device offset
367 gfxPoint offset
= surface
->GetDeviceOffset();
368 surface
->SetDeviceOffset(gfxPoint(offset
.x
- xPos
,
370 // we don't have a temp surface
371 mUpdateSurface
= nullptr;
372 // remember which image to EndUpdate
374 return surface
.get();
378 // Get the real updated region, taking into account the capabilities of
379 // each TextureImage tile
380 GetUpdateRegion(aRegion
);
381 mUpdateRegion
= aRegion
;
382 bounds
= aRegion
.GetBounds();
384 // update covers multiple Images - create a temp surface to paint in
385 gfxASurface::gfxImageFormat format
=
386 (GetContentType() == gfxASurface::CONTENT_COLOR
) ?
387 gfxASurface::ImageFormatRGB24
: gfxASurface::ImageFormatARGB32
;
388 mUpdateSurface
= gfxPlatform::GetPlatform()->
389 CreateOffscreenSurface(gfxIntSize(bounds
.width
, bounds
.height
), gfxASurface::ContentFromFormat(format
));
390 mUpdateSurface
->SetDeviceOffset(gfxPoint(-bounds
.x
, -bounds
.y
));
392 return mUpdateSurface
;
396 TiledTextureImage::EndUpdate()
398 NS_ASSERTION(mInUpdate
, "EndUpdate not in update");
399 if (!mUpdateSurface
) { // update was to a single TextureImage
400 mImages
[mCurrentImage
]->EndUpdate();
402 mTextureState
= Valid
;
403 mTextureFormat
= mImages
[mCurrentImage
]->GetTextureFormat();
407 // upload tiles from temp surface
408 for (unsigned i
= 0; i
< mImages
.Length(); i
++) {
409 int xPos
= (i
% mColumns
) * mTileSize
;
410 int yPos
= (i
/ mColumns
) * mTileSize
;
411 nsIntRect imageRect
= nsIntRect(nsIntPoint(xPos
,yPos
), mImages
[i
]->GetSize());
413 nsIntRegion subregion
;
414 subregion
.And(mUpdateRegion
, imageRect
);
415 if (subregion
.IsEmpty())
417 subregion
.MoveBy(-xPos
, -yPos
); // Tile-local space
418 // copy tile from temp surface
419 gfxASurface
* surf
= mImages
[i
]->BeginUpdate(subregion
);
420 nsRefPtr
<gfxContext
> ctx
= new gfxContext(surf
);
421 gfxUtils::ClipToRegion(ctx
, subregion
);
422 ctx
->SetOperator(gfxContext::OPERATOR_SOURCE
);
423 ctx
->SetSource(mUpdateSurface
, gfxPoint(-xPos
, -yPos
));
425 mImages
[i
]->EndUpdate();
428 mUpdateSurface
= nullptr;
430 mTextureFormat
= mImages
[0]->GetTextureFormat();
431 mTextureState
= Valid
;
434 void TiledTextureImage::BeginTileIteration()
439 bool TiledTextureImage::NextTile()
441 bool continueIteration
= true;
443 if (mIterationCallback
)
444 continueIteration
= mIterationCallback(this, mCurrentImage
,
445 mIterationCallbackData
);
447 if (mCurrentImage
+ 1 < mImages
.Length()) {
449 return continueIteration
;
454 void TiledTextureImage::SetIterationCallback(TileIterationCallback aCallback
,
457 mIterationCallback
= aCallback
;
458 mIterationCallbackData
= aCallbackData
;
461 nsIntRect
TiledTextureImage::GetTileRect()
463 if (!GetTileCount()) {
466 nsIntRect rect
= mImages
[mCurrentImage
]->GetTileRect();
467 unsigned int xPos
= (mCurrentImage
% mColumns
) * mTileSize
;
468 unsigned int yPos
= (mCurrentImage
/ mColumns
) * mTileSize
;
469 rect
.MoveBy(xPos
, yPos
);
473 nsIntRect
TiledTextureImage::GetSrcTileRect()
475 nsIntRect rect
= GetTileRect();
476 unsigned int srcY
= mFlags
& NeedsYFlip
477 ? mSize
.height
- rect
.height
- rect
.y
479 return nsIntRect(rect
.x
, srcY
, rect
.width
, rect
.height
);
483 TiledTextureImage::BindTexture(GLenum aTextureUnit
)
485 if (!GetTileCount()) {
488 mImages
[mCurrentImage
]->BindTexture(aTextureUnit
);
492 TiledTextureImage::ApplyFilter()
494 mGL
->ApplyFilterToBoundTexture(mFilter
);
498 * Resize, trying to reuse tiles. The reuse strategy is to decide on reuse per
499 * column. A tile on a column is reused if it hasn't changed size, otherwise it
500 * is discarded/replaced. Extra tiles on a column are pruned after iterating
501 * each column, and extra rows are pruned after iteration over the entire image
504 void TiledTextureImage::Resize(const nsIntSize
& aSize
)
506 if (mSize
== aSize
&& mTextureState
!= Created
) {
510 // calculate rows and columns, rounding up
511 unsigned int columns
= (aSize
.width
+ mTileSize
- 1) / mTileSize
;
512 unsigned int rows
= (aSize
.height
+ mTileSize
- 1) / mTileSize
;
514 // Iterate over old tile-store and insert/remove tiles as necessary
517 for (row
= 0; row
< (int)rows
; row
++) {
518 // If we've gone beyond how many rows there were before, set mColumns to
519 // zero so that we only create new tiles.
520 if (row
>= (int)mRows
)
523 // Similarly, if we're on the last row of old tiles and the height has
524 // changed, discard all tiles in that row.
525 // This will cause the pruning of columns not to work, but we don't need
526 // to worry about that, as no more tiles will be reused past this point
528 if ((row
== (int)mRows
- 1) && (aSize
.height
!= mSize
.height
))
532 for (col
= 0; col
< (int)columns
; col
++) {
533 nsIntSize
size( // use tilesize first, then the remainder
534 (col
+1) * mTileSize
> (unsigned int)aSize
.width
? aSize
.width
% mTileSize
: mTileSize
,
535 (row
+1) * mTileSize
> (unsigned int)aSize
.height
? aSize
.height
% mTileSize
: mTileSize
);
537 bool replace
= false;
539 // Check if we can re-use old tiles.
540 if (col
< (int)mColumns
) {
541 // Reuse an existing tile. If the tile is an end-tile and the
542 // width differs, replace it instead.
543 if (mSize
.width
!= aSize
.width
) {
544 if (col
== (int)mColumns
- 1) {
545 // Tile at the end of the old column, replace it with
548 } else if (col
== (int)columns
- 1) {
549 // Tile at the end of the new column, create a new one.
551 // Before the last column on both the old and new sizes,
552 // reuse existing tile.
557 // Width hasn't changed, reuse existing tile.
563 // Create a new tile.
564 nsRefPtr
<TextureImage
> teximg
=
565 mGL
->TileGenFunc(size
, mContentType
, mFlags
);
567 mImages
.ReplaceElementAt(i
, teximg
.forget());
569 mImages
.InsertElementAt(i
, teximg
.forget());
573 // Prune any unused tiles on the end of the column.
574 if (row
< (int)mRows
) {
575 for (col
= (int)mColumns
- col
; col
> 0; col
--) {
576 mImages
.RemoveElementAt(i
);
581 // Prune any unused tiles at the end of the store.
582 unsigned int length
= mImages
.Length();
583 for (; i
< length
; i
++)
584 mImages
.RemoveElementAt(mImages
.Length()-1);
586 // Reset tile-store properties.
590 mTextureState
= Allocated
;
594 uint32_t TiledTextureImage::GetTileCount()
596 return mImages
.Length();
599 TextureImage::ScopedBindTexture::ScopedBindTexture(TextureImage
* aTexture
,
604 MOZ_ASSERT(aTextureUnit
>= LOCAL_GL_TEXTURE0
);
605 mTexture
->BindTexture(aTextureUnit
);
609 already_AddRefed
<TextureImage
>
610 CreateBasicTextureImage(GLContext
* aGL
,
611 const nsIntSize
& aSize
,
612 TextureImage::ContentType aContentType
,
614 TextureImage::Flags aFlags
)
616 bool useNearestFilter
= aFlags
& TextureImage::UseNearestFilter
;
620 aGL
->fGenTextures(1, &texture
);
622 ScopedBindTexture
bind(aGL
, texture
);
624 GLint texfilter
= useNearestFilter
? LOCAL_GL_NEAREST
: LOCAL_GL_LINEAR
;
625 aGL
->fTexParameteri(LOCAL_GL_TEXTURE_2D
, LOCAL_GL_TEXTURE_MIN_FILTER
, texfilter
);
626 aGL
->fTexParameteri(LOCAL_GL_TEXTURE_2D
, LOCAL_GL_TEXTURE_MAG_FILTER
, texfilter
);
627 aGL
->fTexParameteri(LOCAL_GL_TEXTURE_2D
, LOCAL_GL_TEXTURE_WRAP_S
, aWrapMode
);
628 aGL
->fTexParameteri(LOCAL_GL_TEXTURE_2D
, LOCAL_GL_TEXTURE_WRAP_T
, aWrapMode
);
630 nsRefPtr
<BasicTextureImage
> texImage
=
631 new BasicTextureImage(texture
, aSize
, aWrapMode
, aContentType
, aGL
, aFlags
);
632 return texImage
.forget();