/src/mozilla-central/gfx/gl/GLTextureImage.cpp
Line | Count | Source (jump to first uncovered line) |
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/. */ |
5 | | |
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" |
16 | | |
17 | | #include "TextureImageEGL.h" |
18 | | |
19 | | using namespace mozilla::gfx; |
20 | | |
21 | | namespace mozilla { |
22 | | namespace gl { |
23 | | |
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) |
31 | 0 | { |
32 | 0 | switch (gl->GetContextType()) { |
33 | 0 | case GLContextType::EGL: |
34 | 0 | return CreateTextureImageEGL(gl, aSize, aContentType, aWrapMode, aFlags, aImageFormat); |
35 | 0 | default: { |
36 | 0 | GLint maxTextureSize; |
37 | 0 | gl->fGetIntegerv(LOCAL_GL_MAX_TEXTURE_SIZE, &maxTextureSize); |
38 | 0 | if (aSize.width > maxTextureSize || aSize.height > maxTextureSize) { |
39 | 0 | NS_ASSERTION(aWrapMode == LOCAL_GL_CLAMP_TO_EDGE, "Can't support wrapping with tiles!"); |
40 | 0 | return CreateTiledTextureImage(gl, aSize, aContentType, aFlags, aImageFormat); |
41 | 0 | } else { |
42 | 0 | return CreateBasicTextureImage(gl, aSize, aContentType, aWrapMode, aFlags); |
43 | 0 | } |
44 | 0 | } |
45 | 0 | } |
46 | 0 | } |
47 | | |
48 | | |
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) |
55 | 0 | { |
56 | 0 | switch (gl->GetContextType()) { |
57 | 0 | case GLContextType::EGL: |
58 | 0 | return TileGenFuncEGL(gl, aSize, aContentType, aFlags, aImageFormat); |
59 | 0 | default: |
60 | 0 | return CreateBasicTextureImage(gl, aSize, aContentType, |
61 | 0 | LOCAL_GL_CLAMP_TO_EDGE, aFlags); |
62 | 0 | } |
63 | 0 | } |
64 | | |
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) |
71 | 0 | { |
72 | 0 | return CreateTextureImage(gl, size, contentType, wrapMode, flags); |
73 | 0 | } |
74 | | |
75 | | bool |
76 | | TextureImage::UpdateFromDataSource(gfx::DataSourceSurface* aSurface, |
77 | | const nsIntRegion* aDestRegion, |
78 | | const gfx::IntPoint* aSrcPoint) |
79 | 0 | { |
80 | 0 | nsIntRegion destRegion = aDestRegion ? *aDestRegion |
81 | 0 | : IntRect(0, 0, |
82 | 0 | aSurface->GetSize().width, |
83 | 0 | aSurface->GetSize().height); |
84 | 0 | gfx::IntPoint srcPoint = aSrcPoint ? *aSrcPoint |
85 | 0 | : gfx::IntPoint(0, 0); |
86 | 0 | return DirectUpdate(aSurface, destRegion, srcPoint); |
87 | 0 | } |
88 | | |
89 | 0 | gfx::IntRect TextureImage::GetTileRect() { |
90 | 0 | return gfx::IntRect(gfx::IntPoint(0,0), mSize); |
91 | 0 | } |
92 | | |
93 | 0 | gfx::IntRect TextureImage::GetSrcTileRect() { |
94 | 0 | return GetTileRect(); |
95 | 0 | } |
96 | | |
97 | | void |
98 | | TextureImage::UpdateUploadSize(size_t amount) |
99 | 0 | { |
100 | 0 | if (mUploadSize > 0) { |
101 | 0 | GfxTexturesReporter::UpdateAmount(GfxTexturesReporter::MemoryFreed, mUploadSize); |
102 | 0 | } |
103 | 0 | mUploadSize = amount; |
104 | 0 | GfxTexturesReporter::UpdateAmount(GfxTexturesReporter::MemoryAllocated, mUploadSize); |
105 | 0 | } |
106 | | |
107 | | BasicTextureImage::~BasicTextureImage() |
108 | 0 | { |
109 | 0 | GLContext* ctx = mGLContext; |
110 | 0 | if (ctx->IsDestroyed() || !ctx->IsOwningThreadCurrent()) { |
111 | 0 | ctx = ctx->GetSharedContext(); |
112 | 0 | } |
113 | 0 |
|
114 | 0 | // If we have a context, then we need to delete the texture; |
115 | 0 | // if we don't have a context (either real or shared), |
116 | 0 | // then they went away when the contex was deleted, because it |
117 | 0 | // was the only one that had access to it. |
118 | 0 | if (ctx && ctx->MakeCurrent()) { |
119 | 0 | ctx->fDeleteTextures(1, &mTexture); |
120 | 0 | } |
121 | 0 | } |
122 | | |
123 | | void |
124 | | BasicTextureImage::BindTexture(GLenum aTextureUnit) |
125 | 0 | { |
126 | 0 | mGLContext->fActiveTexture(aTextureUnit); |
127 | 0 | mGLContext->fBindTexture(LOCAL_GL_TEXTURE_2D, mTexture); |
128 | 0 | mGLContext->fActiveTexture(LOCAL_GL_TEXTURE0); |
129 | 0 | } |
130 | | |
131 | | bool |
132 | | BasicTextureImage::DirectUpdate(gfx::DataSourceSurface* aSurf, const nsIntRegion& aRegion, const gfx::IntPoint& aFrom /* = gfx::IntPoint(0, 0) */) |
133 | 0 | { |
134 | 0 | nsIntRegion region; |
135 | 0 | if (mTextureState == Valid) { |
136 | 0 | region = aRegion; |
137 | 0 | } else { |
138 | 0 | region = nsIntRegion(IntRect(0, 0, mSize.width, mSize.height)); |
139 | 0 | } |
140 | 0 | bool needInit = mTextureState == Created; |
141 | 0 | size_t uploadSize; |
142 | 0 |
|
143 | 0 | mTextureFormat = |
144 | 0 | UploadSurfaceToTexture(mGLContext, |
145 | 0 | aSurf, |
146 | 0 | region, |
147 | 0 | mTexture, |
148 | 0 | mSize, |
149 | 0 | &uploadSize, |
150 | 0 | needInit, |
151 | 0 | aFrom); |
152 | 0 | if (mTextureFormat == SurfaceFormat::UNKNOWN) { |
153 | 0 | return false; |
154 | 0 | } |
155 | 0 | |
156 | 0 | if (uploadSize > 0) { |
157 | 0 | UpdateUploadSize(uploadSize); |
158 | 0 | } |
159 | 0 | mTextureState = Valid; |
160 | 0 | return true; |
161 | 0 | } |
162 | | |
163 | | void |
164 | | BasicTextureImage::Resize(const gfx::IntSize& aSize) |
165 | 0 | { |
166 | 0 | mGLContext->fBindTexture(LOCAL_GL_TEXTURE_2D, mTexture); |
167 | 0 |
|
168 | 0 | // This matches the logic in UploadImageDataToTexture so that |
169 | 0 | // we avoid mixing formats. |
170 | 0 | GLenum format; |
171 | 0 | GLenum type; |
172 | 0 | if (mGLContext->GetPreferredARGB32Format() == LOCAL_GL_BGRA) { |
173 | 0 | MOZ_ASSERT(!mGLContext->IsGLES()); |
174 | 0 | format = LOCAL_GL_BGRA; |
175 | 0 | type = LOCAL_GL_UNSIGNED_INT_8_8_8_8_REV; |
176 | 0 | } else { |
177 | 0 | format = LOCAL_GL_RGBA; |
178 | 0 | type = LOCAL_GL_UNSIGNED_BYTE; |
179 | 0 | } |
180 | 0 |
|
181 | 0 | mGLContext->fTexImage2D(LOCAL_GL_TEXTURE_2D, |
182 | 0 | 0, |
183 | 0 | LOCAL_GL_RGBA, |
184 | 0 | aSize.width, |
185 | 0 | aSize.height, |
186 | 0 | 0, |
187 | 0 | format, |
188 | 0 | type, |
189 | 0 | nullptr); |
190 | 0 |
|
191 | 0 | mTextureState = Allocated; |
192 | 0 | mSize = aSize; |
193 | 0 | } |
194 | | |
195 | 0 | gfx::IntSize TextureImage::GetSize() const { |
196 | 0 | return mSize; |
197 | 0 | } |
198 | | |
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) |
209 | 0 | {} |
210 | | |
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) |
221 | 0 | {} |
222 | | |
223 | | static bool |
224 | | WantsSmallTiles(GLContext* gl) |
225 | 0 | { |
226 | 0 | // We can't use small tiles on the SGX 540, because of races in texture upload. |
227 | 0 | if (gl->WorkAroundDriverBugs() && |
228 | 0 | gl->Renderer() == GLRenderer::SGX540) |
229 | 0 | return false; |
230 | 0 | |
231 | 0 | // We should use small tiles for good performance if we can't use |
232 | 0 | // glTexSubImage2D() for some reason. |
233 | 0 | if (!CanUploadSubTextures(gl)) |
234 | 0 | return true; |
235 | 0 | |
236 | 0 | // Don't use small tiles otherwise. (If we implement incremental texture upload, |
237 | 0 | // then we will want to revisit this.) |
238 | 0 | return false; |
239 | 0 | } |
240 | | |
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) |
256 | 0 | { |
257 | 0 | if (!(aFlags & TextureImage::DisallowBigImage) && WantsSmallTiles(mGL)) { |
258 | 0 | mTileSize = 256; |
259 | 0 | } else { |
260 | 0 | mGL->fGetIntegerv(LOCAL_GL_MAX_TEXTURE_SIZE, (GLint*) &mTileSize); |
261 | 0 | } |
262 | 0 | if (aSize.width != 0 && aSize.height != 0) { |
263 | 0 | Resize(aSize); |
264 | 0 | } |
265 | 0 | } |
266 | | |
267 | | TiledTextureImage::~TiledTextureImage() |
268 | 0 | { |
269 | 0 | } |
270 | | |
271 | | bool |
272 | | TiledTextureImage::DirectUpdate(gfx::DataSourceSurface* aSurf, const nsIntRegion& aRegion, const gfx::IntPoint& aFrom /* = gfx::IntPoint(0, 0) */) |
273 | 0 | { |
274 | 0 | if (mSize.width == 0 || mSize.height == 0) { |
275 | 0 | return true; |
276 | 0 | } |
277 | 0 | |
278 | 0 | nsIntRegion region; |
279 | 0 |
|
280 | 0 | if (mTextureState != Valid) { |
281 | 0 | IntRect bounds = IntRect(0, 0, mSize.width, mSize.height); |
282 | 0 | region = nsIntRegion(bounds); |
283 | 0 | } else { |
284 | 0 | region = aRegion; |
285 | 0 | } |
286 | 0 |
|
287 | 0 | bool result = true; |
288 | 0 | int oldCurrentImage = mCurrentImage; |
289 | 0 | BeginBigImageIteration(); |
290 | 0 | do { |
291 | 0 | IntRect tileRect = GetSrcTileRect(); |
292 | 0 | int xPos = tileRect.X(); |
293 | 0 | int yPos = tileRect.Y(); |
294 | 0 |
|
295 | 0 | nsIntRegion tileRegion; |
296 | 0 | tileRegion.And(region, tileRect); // intersect with tile |
297 | 0 |
|
298 | 0 | if (tileRegion.IsEmpty()) |
299 | 0 | continue; |
300 | 0 | |
301 | 0 | tileRegion.MoveBy(-xPos, -yPos); // translate into tile local space |
302 | 0 |
|
303 | 0 | result &= mImages[mCurrentImage]-> |
304 | 0 | DirectUpdate(aSurf, tileRegion, aFrom + gfx::IntPoint(xPos, yPos)); |
305 | 0 |
|
306 | 0 | if (mCurrentImage == mImages.Length() - 1) { |
307 | 0 | // We know we're done, but we still need to ensure that the callback |
308 | 0 | // gets called (e.g. to update the uploaded region). |
309 | 0 | NextTile(); |
310 | 0 | break; |
311 | 0 | } |
312 | 0 | // Override a callback cancelling iteration if the texture wasn't valid. |
313 | 0 | // We need to force the update in that situation, or we may end up |
314 | 0 | // showing invalid/out-of-date texture data. |
315 | 0 | } while (NextTile() || (mTextureState != Valid)); |
316 | 0 | mCurrentImage = oldCurrentImage; |
317 | 0 |
|
318 | 0 | mTextureFormat = mImages[0]->GetTextureFormat(); |
319 | 0 | mTextureState = Valid; |
320 | 0 | return result; |
321 | 0 | } |
322 | | |
323 | | void TiledTextureImage::BeginBigImageIteration() |
324 | 0 | { |
325 | 0 | mCurrentImage = 0; |
326 | 0 | } |
327 | | |
328 | | bool TiledTextureImage::NextTile() |
329 | 0 | { |
330 | 0 | bool continueIteration = true; |
331 | 0 |
|
332 | 0 | if (mIterationCallback) |
333 | 0 | continueIteration = mIterationCallback(this, mCurrentImage, |
334 | 0 | mIterationCallbackData); |
335 | 0 |
|
336 | 0 | if (mCurrentImage + 1 < mImages.Length()) { |
337 | 0 | mCurrentImage++; |
338 | 0 | return continueIteration; |
339 | 0 | } |
340 | 0 | return false; |
341 | 0 | } |
342 | | |
343 | | void TiledTextureImage::SetIterationCallback(BigImageIterationCallback aCallback, |
344 | | void* aCallbackData) |
345 | 0 | { |
346 | 0 | mIterationCallback = aCallback; |
347 | 0 | mIterationCallbackData = aCallbackData; |
348 | 0 | } |
349 | | |
350 | | gfx::IntRect TiledTextureImage::GetTileRect() |
351 | 0 | { |
352 | 0 | if (!GetTileCount()) { |
353 | 0 | return gfx::IntRect(); |
354 | 0 | } |
355 | 0 | gfx::IntRect rect = mImages[mCurrentImage]->GetTileRect(); |
356 | 0 | unsigned int xPos = (mCurrentImage % mColumns) * mTileSize; |
357 | 0 | unsigned int yPos = (mCurrentImage / mColumns) * mTileSize; |
358 | 0 | rect.MoveBy(xPos, yPos); |
359 | 0 | return rect; |
360 | 0 | } |
361 | | |
362 | | gfx::IntRect TiledTextureImage::GetSrcTileRect() |
363 | 0 | { |
364 | 0 | gfx::IntRect rect = GetTileRect(); |
365 | 0 | const bool needsYFlip = mFlags & OriginBottomLeft; |
366 | 0 | unsigned int srcY = needsYFlip ? mSize.height - rect.Height() - rect.Y() |
367 | 0 | : rect.Y(); |
368 | 0 | return gfx::IntRect(rect.X(), srcY, rect.Width(), rect.Height()); |
369 | 0 | } |
370 | | |
371 | | void |
372 | | TiledTextureImage::BindTexture(GLenum aTextureUnit) |
373 | 0 | { |
374 | 0 | if (!GetTileCount()) { |
375 | 0 | return; |
376 | 0 | } |
377 | 0 | mImages[mCurrentImage]->BindTexture(aTextureUnit); |
378 | 0 | } |
379 | | |
380 | | /* |
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. |
386 | | */ |
387 | | void TiledTextureImage::Resize(const gfx::IntSize& aSize) |
388 | 0 | { |
389 | 0 | if (mSize == aSize && mTextureState != Created) { |
390 | 0 | return; |
391 | 0 | } |
392 | 0 | |
393 | 0 | // calculate rows and columns, rounding up |
394 | 0 | unsigned int columns = (aSize.width + mTileSize - 1) / mTileSize; |
395 | 0 | unsigned int rows = (aSize.height + mTileSize - 1) / mTileSize; |
396 | 0 |
|
397 | 0 | // Iterate over old tile-store and insert/remove tiles as necessary |
398 | 0 | int row; |
399 | 0 | unsigned int i = 0; |
400 | 0 | for (row = 0; row < (int)rows; row++) { |
401 | 0 | // If we've gone beyond how many rows there were before, set mColumns to |
402 | 0 | // zero so that we only create new tiles. |
403 | 0 | if (row >= (int)mRows) |
404 | 0 | mColumns = 0; |
405 | 0 |
|
406 | 0 | // Similarly, if we're on the last row of old tiles and the height has |
407 | 0 | // changed, discard all tiles in that row. |
408 | 0 | // This will cause the pruning of columns not to work, but we don't need |
409 | 0 | // to worry about that, as no more tiles will be reused past this point |
410 | 0 | // anyway. |
411 | 0 | if ((row == (int)mRows - 1) && (aSize.height != mSize.height)) |
412 | 0 | mColumns = 0; |
413 | 0 |
|
414 | 0 | int col; |
415 | 0 | for (col = 0; col < (int)columns; col++) { |
416 | 0 | IntSize size( // use tilesize first, then the remainder |
417 | 0 | (col+1) * mTileSize > (unsigned int)aSize.width ? aSize.width % mTileSize : mTileSize, |
418 | 0 | (row+1) * mTileSize > (unsigned int)aSize.height ? aSize.height % mTileSize : mTileSize); |
419 | 0 |
|
420 | 0 | bool replace = false; |
421 | 0 |
|
422 | 0 | // Check if we can re-use old tiles. |
423 | 0 | if (col < (int)mColumns) { |
424 | 0 | // Reuse an existing tile. If the tile is an end-tile and the |
425 | 0 | // width differs, replace it instead. |
426 | 0 | if (mSize.width != aSize.width) { |
427 | 0 | if (col == (int)mColumns - 1) { |
428 | 0 | // Tile at the end of the old column, replace it with |
429 | 0 | // a new one. |
430 | 0 | replace = true; |
431 | 0 | } else if (col == (int)columns - 1) { |
432 | 0 | // Tile at the end of the new column, create a new one. |
433 | 0 | } else { |
434 | 0 | // Before the last column on both the old and new sizes, |
435 | 0 | // reuse existing tile. |
436 | 0 | i++; |
437 | 0 | continue; |
438 | 0 | } |
439 | 0 | } else { |
440 | 0 | // Width hasn't changed, reuse existing tile. |
441 | 0 | i++; |
442 | 0 | continue; |
443 | 0 | } |
444 | 0 | } |
445 | 0 | |
446 | 0 | // Create a new tile. |
447 | 0 | RefPtr<TextureImage> teximg = |
448 | 0 | TileGenFunc(mGL, size, mContentType, mFlags, mImageFormat); |
449 | 0 | if (replace) |
450 | 0 | mImages.ReplaceElementAt(i, teximg); |
451 | 0 | else |
452 | 0 | mImages.InsertElementAt(i, teximg); |
453 | 0 | i++; |
454 | 0 | } |
455 | 0 |
|
456 | 0 | // Prune any unused tiles on the end of the column. |
457 | 0 | if (row < (int)mRows) { |
458 | 0 | for (col = (int)mColumns - col; col > 0; col--) { |
459 | 0 | mImages.RemoveElementAt(i); |
460 | 0 | } |
461 | 0 | } |
462 | 0 | } |
463 | 0 |
|
464 | 0 | // Prune any unused tiles at the end of the store. |
465 | 0 | unsigned int length = mImages.Length(); |
466 | 0 | for (; i < length; i++) |
467 | 0 | mImages.RemoveLastElement(); |
468 | 0 |
|
469 | 0 | // Reset tile-store properties. |
470 | 0 | mRows = rows; |
471 | 0 | mColumns = columns; |
472 | 0 | mSize = aSize; |
473 | 0 | mTextureState = Allocated; |
474 | 0 | mCurrentImage = 0; |
475 | 0 | } |
476 | | |
477 | | uint32_t TiledTextureImage::GetTileCount() |
478 | 0 | { |
479 | 0 | return mImages.Length(); |
480 | 0 | } |
481 | | |
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) |
488 | 0 | { |
489 | 0 | RefPtr<TextureImage> texImage = static_cast<TextureImage*>( |
490 | 0 | new gl::TiledTextureImage(aGL, aSize, aContentType, aFlags, aImageFormat)); |
491 | 0 | return texImage.forget(); |
492 | 0 | } |
493 | | |
494 | | |
495 | | already_AddRefed<TextureImage> |
496 | | CreateBasicTextureImage(GLContext* aGL, |
497 | | const gfx::IntSize& aSize, |
498 | | TextureImage::ContentType aContentType, |
499 | | GLenum aWrapMode, |
500 | | TextureImage::Flags aFlags) |
501 | 0 | { |
502 | 0 | bool useNearestFilter = aFlags & TextureImage::UseNearestFilter; |
503 | 0 | if (!aGL->MakeCurrent()) { |
504 | 0 | return nullptr; |
505 | 0 | } |
506 | 0 | |
507 | 0 | GLuint texture = 0; |
508 | 0 | aGL->fGenTextures(1, &texture); |
509 | 0 |
|
510 | 0 | ScopedBindTexture bind(aGL, texture); |
511 | 0 |
|
512 | 0 | GLint texfilter = useNearestFilter ? LOCAL_GL_NEAREST : LOCAL_GL_LINEAR; |
513 | 0 | aGL->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_MIN_FILTER, texfilter); |
514 | 0 | aGL->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_MAG_FILTER, texfilter); |
515 | 0 | aGL->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_WRAP_S, aWrapMode); |
516 | 0 | aGL->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_WRAP_T, aWrapMode); |
517 | 0 |
|
518 | 0 | RefPtr<BasicTextureImage> texImage = |
519 | 0 | new BasicTextureImage(texture, aSize, aWrapMode, aContentType, |
520 | 0 | aGL, aFlags); |
521 | 0 | return texImage.forget(); |
522 | 0 | } |
523 | | |
524 | | } // namespace gl |
525 | | } // namespace mozilla |