Bug 1470678 [wpt PR 11640] - Add a test for text-decoration in tables quirk, a=testonly
[gecko.git] / gfx / gl / GLTextureImage.cpp
blob84726e5934fdafb24240e5666ddc5de5db4a6f53
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"
7 #include "GLContext.h"
8 #include "gfxContext.h"
9 #include "gfxPlatform.h"
10 #include "gfxUtils.h"
11 #include "gfx2DGlue.h"
12 #include "mozilla/gfx/2D.h"
13 #include "ScopedGLHelpers.h"
14 #include "GLUploadHelpers.h"
15 #include "GfxTexturesReporter.h"
17 #include "TextureImageEGL.h"
19 using namespace mozilla::gfx;
21 namespace mozilla {
22 namespace gl {
24 already_AddRefed<TextureImage>
25 CreateTextureImage(GLContext* gl,
26 const gfx::IntSize& aSize,
27 TextureImage::ContentType aContentType,
28 GLenum aWrapMode,
29 TextureImage::Flags aFlags,
30 TextureImage::ImageFormat aImageFormat)
32 switch (gl->GetContextType()) {
33 case GLContextType::EGL:
34 return CreateTextureImageEGL(gl, aSize, aContentType, aWrapMode, aFlags, aImageFormat);
35 default: {
36 GLint maxTextureSize;
37 gl->fGetIntegerv(LOCAL_GL_MAX_TEXTURE_SIZE, &maxTextureSize);
38 if (aSize.width > maxTextureSize || aSize.height > maxTextureSize) {
39 NS_ASSERTION(aWrapMode == LOCAL_GL_CLAMP_TO_EDGE, "Can't support wrapping with tiles!");
40 return CreateTiledTextureImage(gl, aSize, aContentType, aFlags, aImageFormat);
41 } else {
42 return CreateBasicTextureImage(gl, aSize, aContentType, aWrapMode, aFlags);
49 static already_AddRefed<TextureImage>
50 TileGenFunc(GLContext* gl,
51 const IntSize& aSize,
52 TextureImage::ContentType aContentType,
53 TextureImage::Flags aFlags,
54 TextureImage::ImageFormat aImageFormat)
56 switch (gl->GetContextType()) {
57 case GLContextType::EGL:
58 return TileGenFuncEGL(gl, aSize, aContentType, aFlags, aImageFormat);
59 default:
60 return CreateBasicTextureImage(gl, aSize, aContentType,
61 LOCAL_GL_CLAMP_TO_EDGE, aFlags);
65 already_AddRefed<TextureImage>
66 TextureImage::Create(GLContext* gl,
67 const gfx::IntSize& size,
68 TextureImage::ContentType contentType,
69 GLenum wrapMode,
70 TextureImage::Flags flags)
72 return CreateTextureImage(gl, size, contentType, wrapMode, flags);
75 bool
76 TextureImage::UpdateFromDataSource(gfx::DataSourceSurface* aSurface,
77 const nsIntRegion* aDestRegion,
78 const gfx::IntPoint* aSrcPoint)
80 nsIntRegion destRegion = aDestRegion ? *aDestRegion
81 : IntRect(0, 0,
82 aSurface->GetSize().width,
83 aSurface->GetSize().height);
84 gfx::IntPoint srcPoint = aSrcPoint ? *aSrcPoint
85 : gfx::IntPoint(0, 0);
86 return DirectUpdate(aSurface, destRegion, srcPoint);
89 gfx::IntRect TextureImage::GetTileRect() {
90 return gfx::IntRect(gfx::IntPoint(0,0), mSize);
93 gfx::IntRect TextureImage::GetSrcTileRect() {
94 return GetTileRect();
97 void
98 TextureImage::UpdateUploadSize(size_t amount)
100 if (mUploadSize > 0) {
101 GfxTexturesReporter::UpdateAmount(GfxTexturesReporter::MemoryFreed, mUploadSize);
103 mUploadSize = amount;
104 GfxTexturesReporter::UpdateAmount(GfxTexturesReporter::MemoryAllocated, mUploadSize);
107 BasicTextureImage::~BasicTextureImage()
109 GLContext* ctx = mGLContext;
110 if (ctx->IsDestroyed() || !ctx->IsOwningThreadCurrent()) {
111 ctx = ctx->GetSharedContext();
114 // If we have a context, then we need to delete the texture;
115 // if we don't have a context (either real or shared),
116 // then they went away when the contex was deleted, because it
117 // was the only one that had access to it.
118 if (ctx && ctx->MakeCurrent()) {
119 ctx->fDeleteTextures(1, &mTexture);
123 void
124 BasicTextureImage::BindTexture(GLenum aTextureUnit)
126 mGLContext->fActiveTexture(aTextureUnit);
127 mGLContext->fBindTexture(LOCAL_GL_TEXTURE_2D, mTexture);
128 mGLContext->fActiveTexture(LOCAL_GL_TEXTURE0);
131 bool
132 BasicTextureImage::DirectUpdate(gfx::DataSourceSurface* aSurf, const nsIntRegion& aRegion, const gfx::IntPoint& aFrom /* = gfx::IntPoint(0, 0) */)
134 nsIntRegion region;
135 if (mTextureState == Valid) {
136 region = aRegion;
137 } else {
138 region = nsIntRegion(IntRect(0, 0, mSize.width, mSize.height));
140 bool needInit = mTextureState == Created;
141 size_t uploadSize;
143 mTextureFormat =
144 UploadSurfaceToTexture(mGLContext,
145 aSurf,
146 region,
147 mTexture,
148 mSize,
149 &uploadSize,
150 needInit,
151 aFrom);
152 if (mTextureFormat == SurfaceFormat::UNKNOWN) {
153 return false;
156 if (uploadSize > 0) {
157 UpdateUploadSize(uploadSize);
159 mTextureState = Valid;
160 return true;
163 void
164 BasicTextureImage::Resize(const gfx::IntSize& aSize)
166 mGLContext->fBindTexture(LOCAL_GL_TEXTURE_2D, mTexture);
168 // This matches the logic in UploadImageDataToTexture so that
169 // we avoid mixing formats.
170 GLenum format;
171 GLenum type;
172 if (mGLContext->GetPreferredARGB32Format() == LOCAL_GL_BGRA) {
173 MOZ_ASSERT(!mGLContext->IsGLES());
174 format = LOCAL_GL_BGRA;
175 type = LOCAL_GL_UNSIGNED_INT_8_8_8_8_REV;
176 } else {
177 format = LOCAL_GL_RGBA;
178 type = LOCAL_GL_UNSIGNED_BYTE;
181 mGLContext->fTexImage2D(LOCAL_GL_TEXTURE_2D,
183 LOCAL_GL_RGBA,
184 aSize.width,
185 aSize.height,
187 format,
188 type,
189 nullptr);
191 mTextureState = Allocated;
192 mSize = aSize;
195 gfx::IntSize TextureImage::GetSize() const {
196 return mSize;
199 TextureImage::TextureImage(const gfx::IntSize& aSize,
200 GLenum aWrapMode, ContentType aContentType,
201 Flags aFlags)
202 : mSize(aSize)
203 , mWrapMode(aWrapMode)
204 , mContentType(aContentType)
205 , mTextureFormat(gfx::SurfaceFormat::UNKNOWN)
206 , mSamplingFilter(SamplingFilter::GOOD)
207 , mFlags(aFlags)
208 , mUploadSize(0)
211 BasicTextureImage::BasicTextureImage(GLuint aTexture,
212 const gfx::IntSize& aSize,
213 GLenum aWrapMode,
214 ContentType aContentType,
215 GLContext* aContext,
216 TextureImage::Flags aFlags)
217 : TextureImage(aSize, aWrapMode, aContentType, aFlags)
218 , mTexture(aTexture)
219 , mTextureState(Created)
220 , mGLContext(aContext)
223 static bool
224 WantsSmallTiles(GLContext* gl)
226 // We can't use small tiles on the SGX 540, because of races in texture upload.
227 if (gl->WorkAroundDriverBugs() &&
228 gl->Renderer() == GLRenderer::SGX540)
229 return false;
231 // We should use small tiles for good performance if we can't use
232 // glTexSubImage2D() for some reason.
233 if (!CanUploadSubTextures(gl))
234 return true;
236 // Don't use small tiles otherwise. (If we implement incremental texture upload,
237 // then we will want to revisit this.)
238 return false;
241 TiledTextureImage::TiledTextureImage(GLContext* aGL,
242 gfx::IntSize aSize,
243 TextureImage::ContentType aContentType,
244 TextureImage::Flags aFlags,
245 TextureImage::ImageFormat aImageFormat)
246 : TextureImage(aSize, LOCAL_GL_CLAMP_TO_EDGE, aContentType, aFlags)
247 , mCurrentImage(0)
248 , mIterationCallback(nullptr)
249 , mIterationCallbackData(nullptr)
250 , mTileSize(0)
251 , mRows(0)
252 , mColumns(0)
253 , mGL(aGL)
254 , mTextureState(Created)
255 , mImageFormat(aImageFormat)
257 if (!(aFlags & TextureImage::DisallowBigImage) && WantsSmallTiles(mGL)) {
258 mTileSize = 256;
259 } else {
260 mGL->fGetIntegerv(LOCAL_GL_MAX_TEXTURE_SIZE, (GLint*) &mTileSize);
262 if (aSize.width != 0 && aSize.height != 0) {
263 Resize(aSize);
267 TiledTextureImage::~TiledTextureImage()
271 bool
272 TiledTextureImage::DirectUpdate(gfx::DataSourceSurface* aSurf, const nsIntRegion& aRegion, const gfx::IntPoint& aFrom /* = gfx::IntPoint(0, 0) */)
274 if (mSize.width == 0 || mSize.height == 0) {
275 return true;
278 nsIntRegion region;
280 if (mTextureState != Valid) {
281 IntRect bounds = IntRect(0, 0, mSize.width, mSize.height);
282 region = nsIntRegion(bounds);
283 } else {
284 region = aRegion;
287 bool result = true;
288 int oldCurrentImage = mCurrentImage;
289 BeginBigImageIteration();
290 do {
291 IntRect tileRect = GetSrcTileRect();
292 int xPos = tileRect.X();
293 int yPos = tileRect.Y();
295 nsIntRegion tileRegion;
296 tileRegion.And(region, tileRect); // intersect with tile
298 if (tileRegion.IsEmpty())
299 continue;
301 tileRegion.MoveBy(-xPos, -yPos); // translate into tile local space
303 result &= mImages[mCurrentImage]->
304 DirectUpdate(aSurf, tileRegion, aFrom + gfx::IntPoint(xPos, yPos));
306 if (mCurrentImage == mImages.Length() - 1) {
307 // We know we're done, but we still need to ensure that the callback
308 // gets called (e.g. to update the uploaded region).
309 NextTile();
310 break;
312 // Override a callback cancelling iteration if the texture wasn't valid.
313 // We need to force the update in that situation, or we may end up
314 // showing invalid/out-of-date texture data.
315 } while (NextTile() || (mTextureState != Valid));
316 mCurrentImage = oldCurrentImage;
318 mTextureFormat = mImages[0]->GetTextureFormat();
319 mTextureState = Valid;
320 return result;
323 void TiledTextureImage::BeginBigImageIteration()
325 mCurrentImage = 0;
328 bool TiledTextureImage::NextTile()
330 bool continueIteration = true;
332 if (mIterationCallback)
333 continueIteration = mIterationCallback(this, mCurrentImage,
334 mIterationCallbackData);
336 if (mCurrentImage + 1 < mImages.Length()) {
337 mCurrentImage++;
338 return continueIteration;
340 return false;
343 void TiledTextureImage::SetIterationCallback(BigImageIterationCallback aCallback,
344 void* aCallbackData)
346 mIterationCallback = aCallback;
347 mIterationCallbackData = aCallbackData;
350 gfx::IntRect TiledTextureImage::GetTileRect()
352 if (!GetTileCount()) {
353 return gfx::IntRect();
355 gfx::IntRect rect = mImages[mCurrentImage]->GetTileRect();
356 unsigned int xPos = (mCurrentImage % mColumns) * mTileSize;
357 unsigned int yPos = (mCurrentImage / mColumns) * mTileSize;
358 rect.MoveBy(xPos, yPos);
359 return rect;
362 gfx::IntRect TiledTextureImage::GetSrcTileRect()
364 gfx::IntRect rect = GetTileRect();
365 const bool needsYFlip = mFlags & OriginBottomLeft;
366 unsigned int srcY = needsYFlip ? mSize.height - rect.Height() - rect.Y()
367 : rect.Y();
368 return gfx::IntRect(rect.X(), srcY, rect.Width(), rect.Height());
371 void
372 TiledTextureImage::BindTexture(GLenum aTextureUnit)
374 if (!GetTileCount()) {
375 return;
377 mImages[mCurrentImage]->BindTexture(aTextureUnit);
381 * Resize, trying to reuse tiles. The reuse strategy is to decide on reuse per
382 * column. A tile on a column is reused if it hasn't changed size, otherwise it
383 * is discarded/replaced. Extra tiles on a column are pruned after iterating
384 * each column, and extra rows are pruned after iteration over the entire image
385 * finishes.
387 void TiledTextureImage::Resize(const gfx::IntSize& aSize)
389 if (mSize == aSize && mTextureState != Created) {
390 return;
393 // calculate rows and columns, rounding up
394 unsigned int columns = (aSize.width + mTileSize - 1) / mTileSize;
395 unsigned int rows = (aSize.height + mTileSize - 1) / mTileSize;
397 // Iterate over old tile-store and insert/remove tiles as necessary
398 int row;
399 unsigned int i = 0;
400 for (row = 0; row < (int)rows; row++) {
401 // If we've gone beyond how many rows there were before, set mColumns to
402 // zero so that we only create new tiles.
403 if (row >= (int)mRows)
404 mColumns = 0;
406 // Similarly, if we're on the last row of old tiles and the height has
407 // changed, discard all tiles in that row.
408 // This will cause the pruning of columns not to work, but we don't need
409 // to worry about that, as no more tiles will be reused past this point
410 // anyway.
411 if ((row == (int)mRows - 1) && (aSize.height != mSize.height))
412 mColumns = 0;
414 int col;
415 for (col = 0; col < (int)columns; col++) {
416 IntSize size( // use tilesize first, then the remainder
417 (col+1) * mTileSize > (unsigned int)aSize.width ? aSize.width % mTileSize : mTileSize,
418 (row+1) * mTileSize > (unsigned int)aSize.height ? aSize.height % mTileSize : mTileSize);
420 bool replace = false;
422 // Check if we can re-use old tiles.
423 if (col < (int)mColumns) {
424 // Reuse an existing tile. If the tile is an end-tile and the
425 // width differs, replace it instead.
426 if (mSize.width != aSize.width) {
427 if (col == (int)mColumns - 1) {
428 // Tile at the end of the old column, replace it with
429 // a new one.
430 replace = true;
431 } else if (col == (int)columns - 1) {
432 // Tile at the end of the new column, create a new one.
433 } else {
434 // Before the last column on both the old and new sizes,
435 // reuse existing tile.
436 i++;
437 continue;
439 } else {
440 // Width hasn't changed, reuse existing tile.
441 i++;
442 continue;
446 // Create a new tile.
447 RefPtr<TextureImage> teximg =
448 TileGenFunc(mGL, size, mContentType, mFlags, mImageFormat);
449 if (replace)
450 mImages.ReplaceElementAt(i, teximg);
451 else
452 mImages.InsertElementAt(i, teximg);
453 i++;
456 // Prune any unused tiles on the end of the column.
457 if (row < (int)mRows) {
458 for (col = (int)mColumns - col; col > 0; col--) {
459 mImages.RemoveElementAt(i);
464 // Prune any unused tiles at the end of the store.
465 unsigned int length = mImages.Length();
466 for (; i < length; i++)
467 mImages.RemoveLastElement();
469 // Reset tile-store properties.
470 mRows = rows;
471 mColumns = columns;
472 mSize = aSize;
473 mTextureState = Allocated;
474 mCurrentImage = 0;
477 uint32_t TiledTextureImage::GetTileCount()
479 return mImages.Length();
482 already_AddRefed<TextureImage>
483 CreateTiledTextureImage(GLContext* aGL,
484 const gfx::IntSize& aSize,
485 TextureImage::ContentType aContentType,
486 TextureImage::Flags aFlags,
487 TextureImage::ImageFormat aImageFormat)
489 RefPtr<TextureImage> texImage = static_cast<TextureImage*>(
490 new gl::TiledTextureImage(aGL, aSize, aContentType, aFlags, aImageFormat));
491 return texImage.forget();
495 already_AddRefed<TextureImage>
496 CreateBasicTextureImage(GLContext* aGL,
497 const gfx::IntSize& aSize,
498 TextureImage::ContentType aContentType,
499 GLenum aWrapMode,
500 TextureImage::Flags aFlags)
502 bool useNearestFilter = aFlags & TextureImage::UseNearestFilter;
503 if (!aGL->MakeCurrent()) {
504 return nullptr;
507 GLuint texture = 0;
508 aGL->fGenTextures(1, &texture);
510 ScopedBindTexture bind(aGL, texture);
512 GLint texfilter = useNearestFilter ? LOCAL_GL_NEAREST : LOCAL_GL_LINEAR;
513 aGL->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_MIN_FILTER, texfilter);
514 aGL->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_MAG_FILTER, texfilter);
515 aGL->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_WRAP_S, aWrapMode);
516 aGL->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_WRAP_T, aWrapMode);
518 RefPtr<BasicTextureImage> texImage =
519 new BasicTextureImage(texture, aSize, aWrapMode, aContentType,
520 aGL, aFlags);
521 return texImage.forget();
524 } // namespace gl
525 } // namespace mozilla