|
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 "ScopedGLHelpers.h" |
|
13 #include "GLUploadHelpers.h" |
|
14 |
|
15 #include "TextureImageEGL.h" |
|
16 #ifdef XP_MACOSX |
|
17 #include "TextureImageCGL.h" |
|
18 #endif |
|
19 |
|
20 namespace mozilla { |
|
21 namespace gl { |
|
22 |
|
23 already_AddRefed<TextureImage> |
|
24 CreateTextureImage(GLContext* gl, |
|
25 const gfx::IntSize& aSize, |
|
26 TextureImage::ContentType aContentType, |
|
27 GLenum aWrapMode, |
|
28 TextureImage::Flags aFlags, |
|
29 TextureImage::ImageFormat aImageFormat) |
|
30 { |
|
31 switch (gl->GetContextType()) { |
|
32 #ifdef XP_MACOSX |
|
33 case GLContextType::CGL: |
|
34 return CreateTextureImageCGL(gl, aSize, aContentType, aWrapMode, aFlags, aImageFormat); |
|
35 #endif |
|
36 case GLContextType::EGL: |
|
37 return CreateTextureImageEGL(gl, aSize, aContentType, aWrapMode, aFlags, aImageFormat); |
|
38 default: |
|
39 return CreateBasicTextureImage(gl, aSize, aContentType, aWrapMode, aFlags, aImageFormat); |
|
40 } |
|
41 } |
|
42 |
|
43 |
|
44 static already_AddRefed<TextureImage> |
|
45 TileGenFunc(GLContext* gl, |
|
46 const nsIntSize& aSize, |
|
47 TextureImage::ContentType aContentType, |
|
48 TextureImage::Flags aFlags, |
|
49 TextureImage::ImageFormat aImageFormat) |
|
50 { |
|
51 switch (gl->GetContextType()) { |
|
52 #ifdef XP_MACOSX |
|
53 case GLContextType::CGL: |
|
54 return TileGenFuncCGL(gl, aSize, aContentType, aFlags, aImageFormat); |
|
55 #endif |
|
56 case GLContextType::EGL: |
|
57 return TileGenFuncEGL(gl, aSize, aContentType, aFlags, aImageFormat); |
|
58 default: |
|
59 return nullptr; |
|
60 } |
|
61 } |
|
62 |
|
63 already_AddRefed<TextureImage> |
|
64 TextureImage::Create(GLContext* gl, |
|
65 const nsIntSize& size, |
|
66 TextureImage::ContentType contentType, |
|
67 GLenum wrapMode, |
|
68 TextureImage::Flags flags) |
|
69 { |
|
70 return Create(gl, size.ToIntSize(), contentType, wrapMode, flags); |
|
71 } |
|
72 |
|
73 // Moz2D equivalent... |
|
74 already_AddRefed<TextureImage> |
|
75 TextureImage::Create(GLContext* gl, |
|
76 const gfx::IntSize& size, |
|
77 TextureImage::ContentType contentType, |
|
78 GLenum wrapMode, |
|
79 TextureImage::Flags flags) |
|
80 { |
|
81 return CreateTextureImage(gl, size, contentType, wrapMode, flags); |
|
82 } |
|
83 |
|
84 bool |
|
85 TextureImage::UpdateFromDataSource(gfx::DataSourceSurface *aSurface, |
|
86 const nsIntRegion* aDestRegion, |
|
87 const gfx::IntPoint* aSrcPoint) |
|
88 { |
|
89 nsIntRegion destRegion = aDestRegion ? *aDestRegion |
|
90 : nsIntRect(0, 0, |
|
91 aSurface->GetSize().width, |
|
92 aSurface->GetSize().height); |
|
93 gfx::IntPoint srcPoint = aSrcPoint ? *aSrcPoint |
|
94 : gfx::IntPoint(0, 0); |
|
95 return DirectUpdate(aSurface, destRegion, srcPoint); |
|
96 } |
|
97 |
|
98 gfx::IntRect TextureImage::GetTileRect() { |
|
99 return gfx::IntRect(gfx::IntPoint(0,0), mSize); |
|
100 } |
|
101 |
|
102 gfx::IntRect TextureImage::GetSrcTileRect() { |
|
103 return GetTileRect(); |
|
104 } |
|
105 |
|
106 BasicTextureImage::~BasicTextureImage() |
|
107 { |
|
108 GLContext *ctx = mGLContext; |
|
109 if (ctx->IsDestroyed() || !ctx->IsOwningThreadCurrent()) { |
|
110 ctx = ctx->GetSharedContext(); |
|
111 } |
|
112 |
|
113 // If we have a context, then we need to delete the texture; |
|
114 // if we don't have a context (either real or shared), |
|
115 // then they went away when the contex was deleted, because it |
|
116 // was the only one that had access to it. |
|
117 if (ctx && ctx->MakeCurrent()) { |
|
118 ctx->fDeleteTextures(1, &mTexture); |
|
119 } |
|
120 } |
|
121 |
|
122 gfx::DrawTarget* |
|
123 BasicTextureImage::BeginUpdate(nsIntRegion& aRegion) |
|
124 { |
|
125 NS_ASSERTION(!mUpdateDrawTarget, "BeginUpdate() without EndUpdate()?"); |
|
126 |
|
127 // determine the region the client will need to repaint |
|
128 if (CanUploadSubTextures(mGLContext)) { |
|
129 GetUpdateRegion(aRegion); |
|
130 } else { |
|
131 aRegion = nsIntRect(nsIntPoint(0, 0), gfx::ThebesIntSize(mSize)); |
|
132 } |
|
133 |
|
134 mUpdateRegion = aRegion; |
|
135 |
|
136 nsIntRect rgnSize = mUpdateRegion.GetBounds(); |
|
137 if (!nsIntRect(nsIntPoint(0, 0), gfx::ThebesIntSize(mSize)).Contains(rgnSize)) { |
|
138 NS_ERROR("update outside of image"); |
|
139 return nullptr; |
|
140 } |
|
141 |
|
142 gfx::SurfaceFormat format = |
|
143 (GetContentType() == gfxContentType::COLOR) ? |
|
144 gfx::SurfaceFormat::B8G8R8X8 : gfx::SurfaceFormat::B8G8R8A8; |
|
145 mUpdateDrawTarget = |
|
146 GetDrawTargetForUpdate(gfx::IntSize(rgnSize.width, rgnSize.height), format); |
|
147 |
|
148 return mUpdateDrawTarget; |
|
149 } |
|
150 |
|
151 void |
|
152 BasicTextureImage::GetUpdateRegion(nsIntRegion& aForRegion) |
|
153 { |
|
154 // if the texture hasn't been initialized yet, or something important |
|
155 // changed, we need to recreate our backing surface and force the |
|
156 // client to paint everything |
|
157 if (mTextureState != Valid) |
|
158 aForRegion = nsIntRect(nsIntPoint(0, 0), gfx::ThebesIntSize(mSize)); |
|
159 } |
|
160 |
|
161 void |
|
162 BasicTextureImage::EndUpdate() |
|
163 { |
|
164 NS_ASSERTION(!!mUpdateDrawTarget, "EndUpdate() without BeginUpdate()?"); |
|
165 |
|
166 // FIXME: this is the slow boat. Make me fast (with GLXPixmap?). |
|
167 |
|
168 RefPtr<gfx::SourceSurface> updateSnapshot = mUpdateDrawTarget->Snapshot(); |
|
169 RefPtr<gfx::DataSourceSurface> updateData = updateSnapshot->GetDataSurface(); |
|
170 |
|
171 bool relative = FinishedSurfaceUpdate(); |
|
172 |
|
173 mTextureFormat = |
|
174 UploadSurfaceToTexture(mGLContext, |
|
175 updateData, |
|
176 mUpdateRegion, |
|
177 mTexture, |
|
178 mTextureState == Created, |
|
179 mUpdateOffset, |
|
180 relative); |
|
181 FinishedSurfaceUpload(); |
|
182 |
|
183 mUpdateDrawTarget = nullptr; |
|
184 mTextureState = Valid; |
|
185 } |
|
186 |
|
187 void |
|
188 BasicTextureImage::BindTexture(GLenum aTextureUnit) |
|
189 { |
|
190 mGLContext->fActiveTexture(aTextureUnit); |
|
191 mGLContext->fBindTexture(LOCAL_GL_TEXTURE_2D, mTexture); |
|
192 mGLContext->fActiveTexture(LOCAL_GL_TEXTURE0); |
|
193 } |
|
194 |
|
195 TemporaryRef<gfx::DrawTarget> |
|
196 BasicTextureImage::GetDrawTargetForUpdate(const gfx::IntSize& aSize, gfx::SurfaceFormat aFmt) |
|
197 { |
|
198 return gfx::Factory::CreateDrawTarget(gfx::BackendType::CAIRO, aSize, aFmt); |
|
199 } |
|
200 |
|
201 bool |
|
202 BasicTextureImage::FinishedSurfaceUpdate() |
|
203 { |
|
204 return false; |
|
205 } |
|
206 |
|
207 void |
|
208 BasicTextureImage::FinishedSurfaceUpload() |
|
209 { |
|
210 } |
|
211 |
|
212 bool |
|
213 BasicTextureImage::DirectUpdate(gfx::DataSourceSurface* aSurf, const nsIntRegion& aRegion, const gfx::IntPoint& aFrom /* = gfx::IntPoint(0, 0) */) |
|
214 { |
|
215 nsIntRect bounds = aRegion.GetBounds(); |
|
216 nsIntRegion region; |
|
217 if (mTextureState != Valid) { |
|
218 bounds = nsIntRect(0, 0, mSize.width, mSize.height); |
|
219 region = nsIntRegion(bounds); |
|
220 } else { |
|
221 region = aRegion; |
|
222 } |
|
223 |
|
224 mTextureFormat = |
|
225 UploadSurfaceToTexture(mGLContext, |
|
226 aSurf, |
|
227 region, |
|
228 mTexture, |
|
229 mTextureState == Created, |
|
230 bounds.TopLeft() + nsIntPoint(aFrom.x, aFrom.y), |
|
231 false); |
|
232 mTextureState = Valid; |
|
233 return true; |
|
234 } |
|
235 |
|
236 void |
|
237 BasicTextureImage::Resize(const gfx::IntSize& aSize) |
|
238 { |
|
239 NS_ASSERTION(!mUpdateDrawTarget, "Resize() while in update?"); |
|
240 |
|
241 mGLContext->fBindTexture(LOCAL_GL_TEXTURE_2D, mTexture); |
|
242 |
|
243 mGLContext->fTexImage2D(LOCAL_GL_TEXTURE_2D, |
|
244 0, |
|
245 LOCAL_GL_RGBA, |
|
246 aSize.width, |
|
247 aSize.height, |
|
248 0, |
|
249 LOCAL_GL_RGBA, |
|
250 LOCAL_GL_UNSIGNED_BYTE, |
|
251 nullptr); |
|
252 |
|
253 mTextureState = Allocated; |
|
254 mSize = aSize; |
|
255 } |
|
256 |
|
257 gfx::IntSize TextureImage::GetSize() const { |
|
258 return mSize; |
|
259 } |
|
260 |
|
261 TextureImage::TextureImage(const gfx::IntSize& aSize, |
|
262 GLenum aWrapMode, ContentType aContentType, |
|
263 Flags aFlags) |
|
264 : mSize(aSize) |
|
265 , mWrapMode(aWrapMode) |
|
266 , mContentType(aContentType) |
|
267 , mFilter(GraphicsFilter::FILTER_GOOD) |
|
268 , mFlags(aFlags) |
|
269 {} |
|
270 |
|
271 BasicTextureImage::BasicTextureImage(GLuint aTexture, |
|
272 const nsIntSize& aSize, |
|
273 GLenum aWrapMode, |
|
274 ContentType aContentType, |
|
275 GLContext* aContext, |
|
276 TextureImage::Flags aFlags /* = TextureImage::NoFlags */, |
|
277 TextureImage::ImageFormat aImageFormat /* = gfxImageFormat::Unknown */) |
|
278 : TextureImage(aSize, aWrapMode, aContentType, aFlags, aImageFormat) |
|
279 , mTexture(aTexture) |
|
280 , mTextureState(Created) |
|
281 , mGLContext(aContext) |
|
282 , mUpdateOffset(0, 0) |
|
283 { |
|
284 } |
|
285 |
|
286 BasicTextureImage::BasicTextureImage(GLuint aTexture, |
|
287 const gfx::IntSize& aSize, |
|
288 GLenum aWrapMode, |
|
289 ContentType aContentType, |
|
290 GLContext* aContext, |
|
291 TextureImage::Flags aFlags, |
|
292 TextureImage::ImageFormat aImageFormat) |
|
293 : TextureImage(ThebesIntSize(aSize), aWrapMode, aContentType, aFlags, aImageFormat) |
|
294 , mTexture(aTexture) |
|
295 , mTextureState(Created) |
|
296 , mGLContext(aContext) |
|
297 , mUpdateOffset(0, 0) |
|
298 {} |
|
299 |
|
300 static bool |
|
301 WantsSmallTiles(GLContext* gl) |
|
302 { |
|
303 // We must use small tiles for good performance if we can't use |
|
304 // glTexSubImage2D() for some reason. |
|
305 if (!CanUploadSubTextures(gl)) |
|
306 return true; |
|
307 |
|
308 // We can't use small tiles on the SGX 540, because of races in texture upload. |
|
309 if (gl->WorkAroundDriverBugs() && |
|
310 gl->Renderer() == GLRenderer::SGX540) |
|
311 return false; |
|
312 |
|
313 // Don't use small tiles otherwise. (If we implement incremental texture upload, |
|
314 // then we will want to revisit this.) |
|
315 return false; |
|
316 } |
|
317 |
|
318 TiledTextureImage::TiledTextureImage(GLContext* aGL, |
|
319 gfx::IntSize aSize, |
|
320 TextureImage::ContentType aContentType, |
|
321 TextureImage::Flags aFlags, |
|
322 TextureImage::ImageFormat aImageFormat) |
|
323 : TextureImage(aSize, LOCAL_GL_CLAMP_TO_EDGE, aContentType, aFlags) |
|
324 , mCurrentImage(0) |
|
325 , mIterationCallback(nullptr) |
|
326 , mInUpdate(false) |
|
327 , mRows(0) |
|
328 , mColumns(0) |
|
329 , mGL(aGL) |
|
330 , mTextureState(Created) |
|
331 , mImageFormat(aImageFormat) |
|
332 { |
|
333 if (!(aFlags & TextureImage::DisallowBigImage) && WantsSmallTiles(mGL)) { |
|
334 mTileSize = 256; |
|
335 } else { |
|
336 mGL->fGetIntegerv(LOCAL_GL_MAX_TEXTURE_SIZE, (GLint*) &mTileSize); |
|
337 } |
|
338 if (aSize.width != 0 && aSize.height != 0) { |
|
339 Resize(aSize); |
|
340 } |
|
341 } |
|
342 |
|
343 TiledTextureImage::~TiledTextureImage() |
|
344 { |
|
345 } |
|
346 |
|
347 bool |
|
348 TiledTextureImage::DirectUpdate(gfx::DataSourceSurface* aSurf, const nsIntRegion& aRegion, const gfx::IntPoint& aFrom /* = gfx::IntPoint(0, 0) */) |
|
349 { |
|
350 if (mSize.width == 0 || mSize.height == 0) { |
|
351 return true; |
|
352 } |
|
353 |
|
354 nsIntRegion region; |
|
355 |
|
356 if (mTextureState != Valid) { |
|
357 nsIntRect bounds = nsIntRect(0, 0, mSize.width, mSize.height); |
|
358 region = nsIntRegion(bounds); |
|
359 } else { |
|
360 region = aRegion; |
|
361 } |
|
362 |
|
363 bool result = true; |
|
364 int oldCurrentImage = mCurrentImage; |
|
365 BeginTileIteration(); |
|
366 do { |
|
367 nsIntRect tileRect = ThebesIntRect(GetSrcTileRect()); |
|
368 int xPos = tileRect.x; |
|
369 int yPos = tileRect.y; |
|
370 |
|
371 nsIntRegion tileRegion; |
|
372 tileRegion.And(region, tileRect); // intersect with tile |
|
373 |
|
374 if (tileRegion.IsEmpty()) |
|
375 continue; |
|
376 |
|
377 if (CanUploadSubTextures(mGL)) { |
|
378 tileRegion.MoveBy(-xPos, -yPos); // translate into tile local space |
|
379 } else { |
|
380 // If sub-textures are unsupported, expand to tile boundaries |
|
381 tileRect.x = tileRect.y = 0; |
|
382 tileRegion = nsIntRegion(tileRect); |
|
383 } |
|
384 |
|
385 result &= mImages[mCurrentImage]-> |
|
386 DirectUpdate(aSurf, tileRegion, aFrom + gfx::IntPoint(xPos, yPos)); |
|
387 |
|
388 if (mCurrentImage == mImages.Length() - 1) { |
|
389 // We know we're done, but we still need to ensure that the callback |
|
390 // gets called (e.g. to update the uploaded region). |
|
391 NextTile(); |
|
392 break; |
|
393 } |
|
394 // Override a callback cancelling iteration if the texture wasn't valid. |
|
395 // We need to force the update in that situation, or we may end up |
|
396 // showing invalid/out-of-date texture data. |
|
397 } while (NextTile() || (mTextureState != Valid)); |
|
398 mCurrentImage = oldCurrentImage; |
|
399 |
|
400 mTextureFormat = mImages[0]->GetTextureFormat(); |
|
401 mTextureState = Valid; |
|
402 return result; |
|
403 } |
|
404 |
|
405 void |
|
406 TiledTextureImage::GetUpdateRegion(nsIntRegion& aForRegion) |
|
407 { |
|
408 if (mTextureState != Valid) { |
|
409 // if the texture hasn't been initialized yet, or something important |
|
410 // changed, we need to recreate our backing surface and force the |
|
411 // client to paint everything |
|
412 aForRegion = nsIntRect(nsIntPoint(0, 0), gfx::ThebesIntSize(mSize)); |
|
413 return; |
|
414 } |
|
415 |
|
416 nsIntRegion newRegion; |
|
417 |
|
418 // We need to query each texture with the region it will be drawing and |
|
419 // set aForRegion to be the combination of all of these regions |
|
420 for (unsigned i = 0; i < mImages.Length(); i++) { |
|
421 int xPos = (i % mColumns) * mTileSize; |
|
422 int yPos = (i / mColumns) * mTileSize; |
|
423 nsIntRect imageRect = nsIntRect(nsIntPoint(xPos,yPos), |
|
424 ThebesIntSize(mImages[i]->GetSize())); |
|
425 |
|
426 if (aForRegion.Intersects(imageRect)) { |
|
427 // Make a copy of the region |
|
428 nsIntRegion subRegion; |
|
429 subRegion.And(aForRegion, imageRect); |
|
430 // Translate it into tile-space |
|
431 subRegion.MoveBy(-xPos, -yPos); |
|
432 // Query region |
|
433 mImages[i]->GetUpdateRegion(subRegion); |
|
434 // Translate back |
|
435 subRegion.MoveBy(xPos, yPos); |
|
436 // Add to the accumulated region |
|
437 newRegion.Or(newRegion, subRegion); |
|
438 } |
|
439 } |
|
440 |
|
441 aForRegion = newRegion; |
|
442 } |
|
443 |
|
444 gfx::DrawTarget* |
|
445 TiledTextureImage::BeginUpdate(nsIntRegion& aRegion) |
|
446 { |
|
447 NS_ASSERTION(!mInUpdate, "nested update"); |
|
448 mInUpdate = true; |
|
449 |
|
450 // Note, we don't call GetUpdateRegion here as if the updated region is |
|
451 // fully contained in a single tile, we get to avoid iterating through |
|
452 // the tiles again (and a little copying). |
|
453 if (mTextureState != Valid) |
|
454 { |
|
455 // if the texture hasn't been initialized yet, or something important |
|
456 // changed, we need to recreate our backing surface and force the |
|
457 // client to paint everything |
|
458 aRegion = nsIntRect(nsIntPoint(0, 0), gfx::ThebesIntSize(mSize)); |
|
459 } |
|
460 |
|
461 nsIntRect bounds = aRegion.GetBounds(); |
|
462 |
|
463 for (unsigned i = 0; i < mImages.Length(); i++) { |
|
464 int xPos = (i % mColumns) * mTileSize; |
|
465 int yPos = (i / mColumns) * mTileSize; |
|
466 nsIntRegion imageRegion = |
|
467 nsIntRegion(nsIntRect(nsIntPoint(xPos,yPos), |
|
468 ThebesIntSize(mImages[i]->GetSize()))); |
|
469 |
|
470 // a single Image can handle this update request |
|
471 if (imageRegion.Contains(aRegion)) { |
|
472 // adjust for tile offset |
|
473 aRegion.MoveBy(-xPos, -yPos); |
|
474 // forward the actual call |
|
475 RefPtr<gfx::DrawTarget> drawTarget = mImages[i]->BeginUpdate(aRegion); |
|
476 // caller expects container space |
|
477 aRegion.MoveBy(xPos, yPos); |
|
478 // we don't have a temp surface |
|
479 mUpdateDrawTarget = nullptr; |
|
480 // remember which image to EndUpdate |
|
481 mCurrentImage = i; |
|
482 return drawTarget.get(); |
|
483 } |
|
484 } |
|
485 |
|
486 // Get the real updated region, taking into account the capabilities of |
|
487 // each TextureImage tile |
|
488 GetUpdateRegion(aRegion); |
|
489 mUpdateRegion = aRegion; |
|
490 bounds = aRegion.GetBounds(); |
|
491 |
|
492 // update covers multiple Images - create a temp surface to paint in |
|
493 gfx::SurfaceFormat format = |
|
494 (GetContentType() == gfxContentType::COLOR) ? |
|
495 gfx::SurfaceFormat::B8G8R8X8: gfx::SurfaceFormat::B8G8R8A8; |
|
496 mUpdateDrawTarget = gfx::Factory::CreateDrawTarget(gfx::BackendType::CAIRO, |
|
497 bounds.Size().ToIntSize(), |
|
498 format); |
|
499 |
|
500 return mUpdateDrawTarget;; |
|
501 } |
|
502 |
|
503 void |
|
504 TiledTextureImage::EndUpdate() |
|
505 { |
|
506 NS_ASSERTION(mInUpdate, "EndUpdate not in update"); |
|
507 if (!mUpdateDrawTarget) { // update was to a single TextureImage |
|
508 mImages[mCurrentImage]->EndUpdate(); |
|
509 mInUpdate = false; |
|
510 mTextureState = Valid; |
|
511 mTextureFormat = mImages[mCurrentImage]->GetTextureFormat(); |
|
512 return; |
|
513 } |
|
514 |
|
515 RefPtr<gfx::SourceSurface> updateSnapshot = mUpdateDrawTarget->Snapshot(); |
|
516 RefPtr<gfx::DataSourceSurface> updateData = updateSnapshot->GetDataSurface(); |
|
517 nsRefPtr<gfxASurface> updateSurface = new gfxImageSurface(updateData->GetData(), |
|
518 gfx::ThebesIntSize(updateData->GetSize()), |
|
519 updateData->Stride(), |
|
520 gfx::SurfaceFormatToImageFormat(updateData->GetFormat())); |
|
521 |
|
522 // upload tiles from temp surface |
|
523 for (unsigned i = 0; i < mImages.Length(); i++) { |
|
524 int xPos = (i % mColumns) * mTileSize; |
|
525 int yPos = (i / mColumns) * mTileSize; |
|
526 nsIntRect imageRect = nsIntRect(nsIntPoint(xPos,yPos), |
|
527 ThebesIntSize(mImages[i]->GetSize())); |
|
528 |
|
529 nsIntRegion subregion; |
|
530 subregion.And(mUpdateRegion, imageRect); |
|
531 if (subregion.IsEmpty()) |
|
532 continue; |
|
533 subregion.MoveBy(-xPos, -yPos); // Tile-local space |
|
534 // copy tile from temp target |
|
535 gfx::DrawTarget* drawTarget = mImages[i]->BeginUpdate(subregion); |
|
536 nsRefPtr<gfxContext> ctx = new gfxContext(drawTarget); |
|
537 gfxUtils::ClipToRegion(ctx, subregion); |
|
538 ctx->SetOperator(gfxContext::OPERATOR_SOURCE); |
|
539 ctx->SetSource(updateSurface, gfxPoint(-xPos, -yPos)); |
|
540 ctx->Paint(); |
|
541 mImages[i]->EndUpdate(); |
|
542 } |
|
543 |
|
544 mUpdateDrawTarget = nullptr; |
|
545 mInUpdate = false; |
|
546 mTextureFormat = mImages[0]->GetTextureFormat(); |
|
547 mTextureState = Valid; |
|
548 } |
|
549 |
|
550 void TiledTextureImage::BeginTileIteration() |
|
551 { |
|
552 mCurrentImage = 0; |
|
553 } |
|
554 |
|
555 bool TiledTextureImage::NextTile() |
|
556 { |
|
557 bool continueIteration = true; |
|
558 |
|
559 if (mIterationCallback) |
|
560 continueIteration = mIterationCallback(this, mCurrentImage, |
|
561 mIterationCallbackData); |
|
562 |
|
563 if (mCurrentImage + 1 < mImages.Length()) { |
|
564 mCurrentImage++; |
|
565 return continueIteration; |
|
566 } |
|
567 return false; |
|
568 } |
|
569 |
|
570 void TiledTextureImage::SetIterationCallback(TileIterationCallback aCallback, |
|
571 void* aCallbackData) |
|
572 { |
|
573 mIterationCallback = aCallback; |
|
574 mIterationCallbackData = aCallbackData; |
|
575 } |
|
576 |
|
577 gfx::IntRect TiledTextureImage::GetTileRect() |
|
578 { |
|
579 if (!GetTileCount()) { |
|
580 return gfx::IntRect(); |
|
581 } |
|
582 gfx::IntRect rect = mImages[mCurrentImage]->GetTileRect(); |
|
583 unsigned int xPos = (mCurrentImage % mColumns) * mTileSize; |
|
584 unsigned int yPos = (mCurrentImage / mColumns) * mTileSize; |
|
585 rect.MoveBy(xPos, yPos); |
|
586 return rect; |
|
587 } |
|
588 |
|
589 gfx::IntRect TiledTextureImage::GetSrcTileRect() |
|
590 { |
|
591 gfx::IntRect rect = GetTileRect(); |
|
592 unsigned int srcY = mFlags & NeedsYFlip |
|
593 ? mSize.height - rect.height - rect.y |
|
594 : rect.y; |
|
595 return gfx::IntRect(rect.x, srcY, rect.width, rect.height); |
|
596 } |
|
597 |
|
598 void |
|
599 TiledTextureImage::BindTexture(GLenum aTextureUnit) |
|
600 { |
|
601 if (!GetTileCount()) { |
|
602 return; |
|
603 } |
|
604 mImages[mCurrentImage]->BindTexture(aTextureUnit); |
|
605 } |
|
606 |
|
607 /* |
|
608 * Resize, trying to reuse tiles. The reuse strategy is to decide on reuse per |
|
609 * column. A tile on a column is reused if it hasn't changed size, otherwise it |
|
610 * is discarded/replaced. Extra tiles on a column are pruned after iterating |
|
611 * each column, and extra rows are pruned after iteration over the entire image |
|
612 * finishes. |
|
613 */ |
|
614 void TiledTextureImage::Resize(const gfx::IntSize& aSize) |
|
615 { |
|
616 if (mSize == aSize && mTextureState != Created) { |
|
617 return; |
|
618 } |
|
619 |
|
620 // calculate rows and columns, rounding up |
|
621 unsigned int columns = (aSize.width + mTileSize - 1) / mTileSize; |
|
622 unsigned int rows = (aSize.height + mTileSize - 1) / mTileSize; |
|
623 |
|
624 // Iterate over old tile-store and insert/remove tiles as necessary |
|
625 int row; |
|
626 unsigned int i = 0; |
|
627 for (row = 0; row < (int)rows; row++) { |
|
628 // If we've gone beyond how many rows there were before, set mColumns to |
|
629 // zero so that we only create new tiles. |
|
630 if (row >= (int)mRows) |
|
631 mColumns = 0; |
|
632 |
|
633 // Similarly, if we're on the last row of old tiles and the height has |
|
634 // changed, discard all tiles in that row. |
|
635 // This will cause the pruning of columns not to work, but we don't need |
|
636 // to worry about that, as no more tiles will be reused past this point |
|
637 // anyway. |
|
638 if ((row == (int)mRows - 1) && (aSize.height != mSize.height)) |
|
639 mColumns = 0; |
|
640 |
|
641 int col; |
|
642 for (col = 0; col < (int)columns; col++) { |
|
643 nsIntSize size( // use tilesize first, then the remainder |
|
644 (col+1) * mTileSize > (unsigned int)aSize.width ? aSize.width % mTileSize : mTileSize, |
|
645 (row+1) * mTileSize > (unsigned int)aSize.height ? aSize.height % mTileSize : mTileSize); |
|
646 |
|
647 bool replace = false; |
|
648 |
|
649 // Check if we can re-use old tiles. |
|
650 if (col < (int)mColumns) { |
|
651 // Reuse an existing tile. If the tile is an end-tile and the |
|
652 // width differs, replace it instead. |
|
653 if (mSize.width != aSize.width) { |
|
654 if (col == (int)mColumns - 1) { |
|
655 // Tile at the end of the old column, replace it with |
|
656 // a new one. |
|
657 replace = true; |
|
658 } else if (col == (int)columns - 1) { |
|
659 // Tile at the end of the new column, create a new one. |
|
660 } else { |
|
661 // Before the last column on both the old and new sizes, |
|
662 // reuse existing tile. |
|
663 i++; |
|
664 continue; |
|
665 } |
|
666 } else { |
|
667 // Width hasn't changed, reuse existing tile. |
|
668 i++; |
|
669 continue; |
|
670 } |
|
671 } |
|
672 |
|
673 // Create a new tile. |
|
674 nsRefPtr<TextureImage> teximg = |
|
675 TileGenFunc(mGL, size, mContentType, mFlags, mImageFormat); |
|
676 if (replace) |
|
677 mImages.ReplaceElementAt(i, teximg); |
|
678 else |
|
679 mImages.InsertElementAt(i, teximg); |
|
680 i++; |
|
681 } |
|
682 |
|
683 // Prune any unused tiles on the end of the column. |
|
684 if (row < (int)mRows) { |
|
685 for (col = (int)mColumns - col; col > 0; col--) { |
|
686 mImages.RemoveElementAt(i); |
|
687 } |
|
688 } |
|
689 } |
|
690 |
|
691 // Prune any unused tiles at the end of the store. |
|
692 unsigned int length = mImages.Length(); |
|
693 for (; i < length; i++) |
|
694 mImages.RemoveElementAt(mImages.Length()-1); |
|
695 |
|
696 // Reset tile-store properties. |
|
697 mRows = rows; |
|
698 mColumns = columns; |
|
699 mSize = aSize; |
|
700 mTextureState = Allocated; |
|
701 mCurrentImage = 0; |
|
702 } |
|
703 |
|
704 uint32_t TiledTextureImage::GetTileCount() |
|
705 { |
|
706 return mImages.Length(); |
|
707 } |
|
708 |
|
709 already_AddRefed<TextureImage> |
|
710 CreateBasicTextureImage(GLContext* aGL, |
|
711 const gfx::IntSize& aSize, |
|
712 TextureImage::ContentType aContentType, |
|
713 GLenum aWrapMode, |
|
714 TextureImage::Flags aFlags, |
|
715 TextureImage::ImageFormat aImageFormat) |
|
716 { |
|
717 bool useNearestFilter = aFlags & TextureImage::UseNearestFilter; |
|
718 if (!aGL->MakeCurrent()) { |
|
719 return nullptr; |
|
720 } |
|
721 |
|
722 GLuint texture = 0; |
|
723 aGL->fGenTextures(1, &texture); |
|
724 |
|
725 ScopedBindTexture bind(aGL, texture); |
|
726 |
|
727 GLint texfilter = useNearestFilter ? LOCAL_GL_NEAREST : LOCAL_GL_LINEAR; |
|
728 aGL->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_MIN_FILTER, texfilter); |
|
729 aGL->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_MAG_FILTER, texfilter); |
|
730 aGL->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_WRAP_S, aWrapMode); |
|
731 aGL->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_WRAP_T, aWrapMode); |
|
732 |
|
733 nsRefPtr<BasicTextureImage> texImage = |
|
734 new BasicTextureImage(texture, aSize, aWrapMode, aContentType, |
|
735 aGL, aFlags, aImageFormat); |
|
736 return texImage.forget(); |
|
737 } |
|
738 |
|
739 } // namespace |
|
740 } // namespace |