| |
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 |