/src/mozilla-central/gfx/layers/composite/ImageHost.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
2 | | /* vim: set ts=8 sts=2 et sw=2 tw=80: */ |
3 | | /* This Source Code Form is subject to the terms of the Mozilla Public |
4 | | * License, v. 2.0. If a copy of the MPL was not distributed with this |
5 | | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
6 | | |
7 | | #include "ImageHost.h" |
8 | | |
9 | | #include "LayersLogging.h" // for AppendToString |
10 | | #include "composite/CompositableHost.h" // for CompositableHost, etc |
11 | | #include "ipc/IPCMessageUtils.h" // for null_t |
12 | | #include "mozilla/Move.h" |
13 | | #include "mozilla/layers/Compositor.h" // for Compositor |
14 | | #include "mozilla/layers/Effects.h" // for TexturedEffect, Effect, etc |
15 | | #include "mozilla/layers/LayerManagerComposite.h" // for TexturedEffect, Effect, etc |
16 | | #include "nsAString.h" |
17 | | #include "nsDebug.h" // for NS_WARNING, NS_ASSERTION |
18 | | #include "nsPrintfCString.h" // for nsPrintfCString |
19 | | #include "nsString.h" // for nsAutoCString |
20 | | |
21 | | namespace mozilla { |
22 | | |
23 | | using namespace gfx; |
24 | | |
25 | | namespace layers { |
26 | | |
27 | | class ISurfaceAllocator; |
28 | | |
29 | | ImageHost::ImageHost(const TextureInfo& aTextureInfo) |
30 | | : CompositableHost(aTextureInfo) |
31 | | , ImageComposite() |
32 | | , mLocked(false) |
33 | 0 | {} |
34 | | |
35 | | ImageHost::~ImageHost() |
36 | 0 | { |
37 | 0 | } |
38 | | |
39 | | void |
40 | | ImageHost::UseTextureHost(const nsTArray<TimedTexture>& aTextures) |
41 | 0 | { |
42 | 0 | MOZ_ASSERT(!mLocked); |
43 | 0 |
|
44 | 0 | CompositableHost::UseTextureHost(aTextures); |
45 | 0 | MOZ_ASSERT(aTextures.Length() >= 1); |
46 | 0 |
|
47 | 0 | nsTArray<TimedImage> newImages; |
48 | 0 |
|
49 | 0 | for (uint32_t i = 0; i < aTextures.Length(); ++i) { |
50 | 0 | const TimedTexture& t = aTextures[i]; |
51 | 0 | MOZ_ASSERT(t.mTexture); |
52 | 0 | if (i + 1 < aTextures.Length() && |
53 | 0 | t.mProducerID == mLastProducerID && t.mFrameID < mLastFrameID) { |
54 | 0 | // Ignore frames before a frame that we already composited. We don't |
55 | 0 | // ever want to display these frames. This could be important if |
56 | 0 | // the frame producer adjusts timestamps (e.g. to track the audio clock) |
57 | 0 | // and the new frame times are earlier. |
58 | 0 | continue; |
59 | 0 | } |
60 | 0 | TimedImage& img = *newImages.AppendElement(); |
61 | 0 | img.mTextureHost = t.mTexture; |
62 | 0 | img.mTimeStamp = t.mTimeStamp; |
63 | 0 | img.mPictureRect = t.mPictureRect; |
64 | 0 | img.mFrameID = t.mFrameID; |
65 | 0 | img.mProducerID = t.mProducerID; |
66 | 0 | img.mTextureHost->SetCropRect(img.mPictureRect); |
67 | 0 | img.mTextureHost->Updated(); |
68 | 0 | } |
69 | 0 |
|
70 | 0 | SetImages(std::move(newImages)); |
71 | 0 |
|
72 | 0 | // If we only have one image we can upload it right away, otherwise we'll upload |
73 | 0 | // on-demand during composition after we have picked the proper timestamp. |
74 | 0 | if (ImagesCount() == 1) { |
75 | 0 | SetCurrentTextureHost(GetImage(0)->mTextureHost); |
76 | 0 | } |
77 | 0 |
|
78 | 0 | HostLayerManager* lm = GetLayerManager(); |
79 | 0 |
|
80 | 0 | // Video producers generally send replacement images with the same frameID but |
81 | 0 | // slightly different timestamps in order to sync with the audio clock. This |
82 | 0 | // means that any CompositeUntil() call we made in Composite() may no longer |
83 | 0 | // guarantee that we'll composite until the next frame is ready. Fix that here. |
84 | 0 | if (lm && mLastFrameID >= 0) { |
85 | 0 | for (const auto& img : Images()) { |
86 | 0 | bool frameComesAfter = |
87 | 0 | img.mFrameID > mLastFrameID || img.mProducerID != mLastProducerID; |
88 | 0 | if (frameComesAfter && !img.mTimeStamp.IsNull()) { |
89 | 0 | lm->CompositeUntil(img.mTimeStamp + |
90 | 0 | TimeDuration::FromMilliseconds(BIAS_TIME_MS)); |
91 | 0 | break; |
92 | 0 | } |
93 | 0 | } |
94 | 0 | } |
95 | 0 | } |
96 | | |
97 | | void |
98 | | ImageHost::SetCurrentTextureHost(TextureHost* aTexture) |
99 | 0 | { |
100 | 0 | if (aTexture == mCurrentTextureHost.get()) { |
101 | 0 | return; |
102 | 0 | } |
103 | 0 | |
104 | 0 | bool swapTextureSources = !!mCurrentTextureHost && !!mCurrentTextureSource |
105 | 0 | && mCurrentTextureHost->HasIntermediateBuffer(); |
106 | 0 |
|
107 | 0 | if (swapTextureSources) { |
108 | 0 | auto dataSource = mCurrentTextureSource->AsDataTextureSource(); |
109 | 0 | if (dataSource) { |
110 | 0 | // The current textureHost has an internal buffer in the form of the |
111 | 0 | // DataTextureSource. Removing the ownership of the texture source |
112 | 0 | // will enable the next texture host we bind to the texture source to |
113 | 0 | // acquire it instead of creating a new one. This is desirable in |
114 | 0 | // ImageHost because the current texture won't be used again with the |
115 | 0 | // same content. It wouldn't be desirable with ContentHost for instance, |
116 | 0 | // because the latter reuses the texture's valid regions. |
117 | 0 | dataSource->SetOwner(nullptr); |
118 | 0 | } |
119 | 0 |
|
120 | 0 | RefPtr<TextureSource> tmp = mExtraTextureSource; |
121 | 0 | mExtraTextureSource = mCurrentTextureSource.get(); |
122 | 0 | mCurrentTextureSource = tmp; |
123 | 0 | } else { |
124 | 0 | mExtraTextureSource = nullptr; |
125 | 0 | } |
126 | 0 |
|
127 | 0 | mCurrentTextureHost = aTexture; |
128 | 0 | mCurrentTextureHost->PrepareTextureSource(mCurrentTextureSource); |
129 | 0 | } |
130 | | |
131 | | void |
132 | | ImageHost::CleanupResources() |
133 | 0 | { |
134 | 0 | mExtraTextureSource = nullptr; |
135 | 0 | mCurrentTextureSource = nullptr; |
136 | 0 | mCurrentTextureHost = nullptr; |
137 | 0 | } |
138 | | |
139 | | void |
140 | | ImageHost::RemoveTextureHost(TextureHost* aTexture) |
141 | 0 | { |
142 | 0 | MOZ_ASSERT(!mLocked); |
143 | 0 |
|
144 | 0 | CompositableHost::RemoveTextureHost(aTexture); |
145 | 0 | RemoveImagesWithTextureHost(aTexture); |
146 | 0 | } |
147 | | |
148 | | TimeStamp |
149 | | ImageHost::GetCompositionTime() const |
150 | 0 | { |
151 | 0 | TimeStamp time; |
152 | 0 | if (HostLayerManager* lm = GetLayerManager()) { |
153 | 0 | time = lm->GetCompositionTime(); |
154 | 0 | } |
155 | 0 | return time; |
156 | 0 | } |
157 | | |
158 | | TextureHost* |
159 | | ImageHost::GetAsTextureHost(IntRect* aPictureRect) |
160 | 0 | { |
161 | 0 | const TimedImage* img = ChooseImage(); |
162 | 0 | if (!img) { |
163 | 0 | return nullptr; |
164 | 0 | } |
165 | 0 | SetCurrentTextureHost(img->mTextureHost); |
166 | 0 | if (aPictureRect) { |
167 | 0 | *aPictureRect = img->mPictureRect; |
168 | 0 | } |
169 | 0 | return img->mTextureHost; |
170 | 0 | } |
171 | | |
172 | | void ImageHost::Attach(Layer* aLayer, |
173 | | TextureSourceProvider* aProvider, |
174 | | AttachFlags aFlags) |
175 | 0 | { |
176 | 0 | CompositableHost::Attach(aLayer, aProvider, aFlags); |
177 | 0 | for (const auto& img : Images()) { |
178 | 0 | img.mTextureHost->SetTextureSourceProvider(aProvider); |
179 | 0 | img.mTextureHost->Updated(); |
180 | 0 | } |
181 | 0 | } |
182 | | |
183 | | void |
184 | | ImageHost::Composite(Compositor* aCompositor, |
185 | | LayerComposite* aLayer, |
186 | | EffectChain& aEffectChain, |
187 | | float aOpacity, |
188 | | const gfx::Matrix4x4& aTransform, |
189 | | const gfx::SamplingFilter aSamplingFilter, |
190 | | const gfx::IntRect& aClipRect, |
191 | | const nsIntRegion* aVisibleRegion, |
192 | | const Maybe<gfx::Polygon>& aGeometry) |
193 | 0 | { |
194 | 0 | RenderInfo info; |
195 | 0 | if (!PrepareToRender(aCompositor, &info)) { |
196 | 0 | return; |
197 | 0 | } |
198 | 0 | |
199 | 0 | const TimedImage* img = info.img; |
200 | 0 |
|
201 | 0 | { |
202 | 0 | AutoLockCompositableHost autoLock(this); |
203 | 0 | if (autoLock.Failed()) { |
204 | 0 | NS_WARNING("failed to lock front buffer"); |
205 | 0 | return; |
206 | 0 | } |
207 | 0 |
|
208 | 0 | if (!mCurrentTextureHost->BindTextureSource(mCurrentTextureSource)) { |
209 | 0 | return; |
210 | 0 | } |
211 | 0 | |
212 | 0 | if (!mCurrentTextureSource) { |
213 | 0 | // BindTextureSource above should have returned false! |
214 | 0 | MOZ_ASSERT(false); |
215 | 0 | return; |
216 | 0 | } |
217 | 0 |
|
218 | 0 | bool isAlphaPremultiplied = |
219 | 0 | !(mCurrentTextureHost->GetFlags() & TextureFlags::NON_PREMULTIPLIED); |
220 | 0 | RefPtr<TexturedEffect> effect = |
221 | 0 | CreateTexturedEffect(mCurrentTextureHost, |
222 | 0 | mCurrentTextureSource.get(), aSamplingFilter, isAlphaPremultiplied); |
223 | 0 | if (!effect) { |
224 | 0 | return; |
225 | 0 | } |
226 | 0 | |
227 | 0 | if (!aCompositor->SupportsEffect(effect->mType)) { |
228 | 0 | return; |
229 | 0 | } |
230 | 0 | |
231 | 0 | DiagnosticFlags diagnosticFlags = DiagnosticFlags::IMAGE; |
232 | 0 | if (effect->mType == EffectTypes::NV12) { |
233 | 0 | diagnosticFlags |= DiagnosticFlags::NV12; |
234 | 0 | } else if (effect->mType == EffectTypes::YCBCR) { |
235 | 0 | diagnosticFlags |= DiagnosticFlags::YCBCR; |
236 | 0 | } |
237 | 0 |
|
238 | 0 | aEffectChain.mPrimaryEffect = effect; |
239 | 0 | gfx::Rect pictureRect(0, 0, img->mPictureRect.Width(), img->mPictureRect.Height()); |
240 | 0 | BigImageIterator* it = mCurrentTextureSource->AsBigImageIterator(); |
241 | 0 | if (it) { |
242 | 0 |
|
243 | 0 | // This iteration does not work if we have multiple texture sources here |
244 | 0 | // (e.g. 3 YCbCr textures). There's nothing preventing the different |
245 | 0 | // planes from having different resolutions or tile sizes. For example, a |
246 | 0 | // YCbCr frame could have Cb and Cr planes that are half the resolution of |
247 | 0 | // the Y plane, in such a way that the Y plane overflows the maximum |
248 | 0 | // texture size and the Cb and Cr planes do not. Then the Y plane would be |
249 | 0 | // split into multiple tiles and the Cb and Cr planes would just be one |
250 | 0 | // tile each. |
251 | 0 | // To handle the general case correctly, we'd have to create a grid of |
252 | 0 | // intersected tiles over all planes, and then draw each grid tile using |
253 | 0 | // the corresponding source tiles from all planes, with appropriate |
254 | 0 | // per-plane per-tile texture coords. |
255 | 0 | // DrawQuad currently assumes that all planes use the same texture coords. |
256 | 0 | MOZ_ASSERT(it->GetTileCount() == 1 || !mCurrentTextureSource->GetNextSibling(), |
257 | 0 | "Can't handle multi-plane BigImages"); |
258 | 0 |
|
259 | 0 | it->BeginBigImageIteration(); |
260 | 0 | do { |
261 | 0 | IntRect tileRect = it->GetTileRect(); |
262 | 0 | gfx::Rect rect(tileRect.X(), tileRect.Y(), tileRect.Width(), tileRect.Height()); |
263 | 0 | rect = rect.Intersect(pictureRect); |
264 | 0 | effect->mTextureCoords = Rect(Float(rect.X() - tileRect.X()) / tileRect.Width(), |
265 | 0 | Float(rect.Y() - tileRect.Y()) / tileRect.Height(), |
266 | 0 | Float(rect.Width()) / tileRect.Width(), |
267 | 0 | Float(rect.Height()) / tileRect.Height()); |
268 | 0 | if (img->mTextureHost->GetFlags() & TextureFlags::ORIGIN_BOTTOM_LEFT) { |
269 | 0 | effect->mTextureCoords.SetRectY(effect->mTextureCoords.YMost(), |
270 | 0 | -effect->mTextureCoords.Height()); |
271 | 0 | } |
272 | 0 | aCompositor->DrawGeometry(rect, aClipRect, aEffectChain, |
273 | 0 | aOpacity, aTransform, aGeometry); |
274 | 0 | aCompositor->DrawDiagnostics(diagnosticFlags | DiagnosticFlags::BIGIMAGE, |
275 | 0 | rect, aClipRect, aTransform, mFlashCounter); |
276 | 0 | } while (it->NextTile()); |
277 | 0 | it->EndBigImageIteration(); |
278 | 0 | // layer border |
279 | 0 | aCompositor->DrawDiagnostics(diagnosticFlags, pictureRect, |
280 | 0 | aClipRect, aTransform, mFlashCounter); |
281 | 0 | } else { |
282 | 0 | IntSize textureSize = mCurrentTextureSource->GetSize(); |
283 | 0 | effect->mTextureCoords = Rect(Float(img->mPictureRect.X()) / textureSize.width, |
284 | 0 | Float(img->mPictureRect.Y()) / textureSize.height, |
285 | 0 | Float(img->mPictureRect.Width()) / textureSize.width, |
286 | 0 | Float(img->mPictureRect.Height()) / textureSize.height); |
287 | 0 |
|
288 | 0 | if (img->mTextureHost->GetFlags() & TextureFlags::ORIGIN_BOTTOM_LEFT) { |
289 | 0 | effect->mTextureCoords.SetRectY(effect->mTextureCoords.YMost(), |
290 | 0 | -effect->mTextureCoords.Height()); |
291 | 0 | } |
292 | 0 |
|
293 | 0 | aCompositor->DrawGeometry(pictureRect, aClipRect, aEffectChain, |
294 | 0 | aOpacity, aTransform, aGeometry); |
295 | 0 | aCompositor->DrawDiagnostics(diagnosticFlags, |
296 | 0 | pictureRect, aClipRect, |
297 | 0 | aTransform, mFlashCounter); |
298 | 0 | } |
299 | 0 | } |
300 | 0 |
|
301 | 0 | FinishRendering(info); |
302 | 0 | } |
303 | | |
304 | | bool |
305 | | ImageHost::PrepareToRender(TextureSourceProvider* aProvider, RenderInfo* aOutInfo) |
306 | 0 | { |
307 | 0 | HostLayerManager* lm = GetLayerManager(); |
308 | 0 | if (!lm) { |
309 | 0 | return false; |
310 | 0 | } |
311 | 0 | |
312 | 0 | int imageIndex = ChooseImageIndex(); |
313 | 0 | if (imageIndex < 0) { |
314 | 0 | return false; |
315 | 0 | } |
316 | 0 | |
317 | 0 | if (uint32_t(imageIndex) + 1 < ImagesCount()) { |
318 | 0 | lm->CompositeUntil(GetImage(imageIndex + 1)->mTimeStamp + |
319 | 0 | TimeDuration::FromMilliseconds(BIAS_TIME_MS)); |
320 | 0 | } |
321 | 0 |
|
322 | 0 | const TimedImage* img = GetImage(imageIndex); |
323 | 0 | img->mTextureHost->SetTextureSourceProvider(aProvider); |
324 | 0 | SetCurrentTextureHost(img->mTextureHost); |
325 | 0 |
|
326 | 0 | aOutInfo->imageIndex = imageIndex; |
327 | 0 | aOutInfo->img = img; |
328 | 0 | aOutInfo->host = mCurrentTextureHost; |
329 | 0 | return true; |
330 | 0 | } |
331 | | |
332 | | RefPtr<TextureSource> |
333 | | ImageHost::AcquireTextureSource(const RenderInfo& aInfo) |
334 | 0 | { |
335 | 0 | MOZ_ASSERT(aInfo.host == mCurrentTextureHost); |
336 | 0 | if (!aInfo.host->AcquireTextureSource(mCurrentTextureSource)) { |
337 | 0 | return nullptr; |
338 | 0 | } |
339 | 0 | return mCurrentTextureSource.get(); |
340 | 0 | } |
341 | | |
342 | | void |
343 | | ImageHost::FinishRendering(const RenderInfo& aInfo) |
344 | 0 | { |
345 | 0 | HostLayerManager* lm = GetLayerManager(); |
346 | 0 | const TimedImage* img = aInfo.img; |
347 | 0 | int imageIndex = aInfo.imageIndex; |
348 | 0 |
|
349 | 0 | if (mLastFrameID != img->mFrameID || mLastProducerID != img->mProducerID) { |
350 | 0 | if (mAsyncRef) { |
351 | 0 | ImageCompositeNotificationInfo info; |
352 | 0 | info.mImageBridgeProcessId = mAsyncRef.mProcessId; |
353 | 0 | info.mNotification = ImageCompositeNotification( |
354 | 0 | mAsyncRef.mHandle, |
355 | 0 | img->mTimeStamp, lm->GetCompositionTime(), |
356 | 0 | img->mFrameID, img->mProducerID); |
357 | 0 | lm->AppendImageCompositeNotification(info); |
358 | 0 | } |
359 | 0 | mLastFrameID = img->mFrameID; |
360 | 0 | mLastProducerID = img->mProducerID; |
361 | 0 | } |
362 | 0 |
|
363 | 0 | // Update mBias last. This can change which frame ChooseImage(Index) would |
364 | 0 | // return, and we don't want to do that until we've finished compositing |
365 | 0 | // since callers of ChooseImage(Index) assume the same image will be chosen |
366 | 0 | // during a given composition. This must happen after autoLock's |
367 | 0 | // destructor! |
368 | 0 | UpdateBias(imageIndex); |
369 | 0 | } |
370 | | |
371 | | void |
372 | | ImageHost::SetTextureSourceProvider(TextureSourceProvider* aProvider) |
373 | 0 | { |
374 | 0 | if (mTextureSourceProvider != aProvider) { |
375 | 0 | for (const auto& img : Images()) { |
376 | 0 | img.mTextureHost->SetTextureSourceProvider(aProvider); |
377 | 0 | } |
378 | 0 | } |
379 | 0 | CompositableHost::SetTextureSourceProvider(aProvider); |
380 | 0 | } |
381 | | |
382 | | void |
383 | | ImageHost::PrintInfo(std::stringstream& aStream, const char* aPrefix) |
384 | 0 | { |
385 | 0 | aStream << aPrefix; |
386 | 0 | aStream << nsPrintfCString("ImageHost (0x%p)", this).get(); |
387 | 0 |
|
388 | 0 | nsAutoCString pfx(aPrefix); |
389 | 0 | pfx += " "; |
390 | 0 | for (const auto& img : Images()) { |
391 | 0 | aStream << "\n"; |
392 | 0 | img.mTextureHost->PrintInfo(aStream, pfx.get()); |
393 | 0 | AppendToString(aStream, img.mPictureRect, " [picture-rect=", "]"); |
394 | 0 | } |
395 | 0 | } |
396 | | |
397 | | void |
398 | | ImageHost::Dump(std::stringstream& aStream, |
399 | | const char* aPrefix, |
400 | | bool aDumpHtml) |
401 | 0 | { |
402 | 0 | for (const auto& img : Images()) { |
403 | 0 | aStream << aPrefix; |
404 | 0 | aStream << (aDumpHtml ? "<ul><li>TextureHost: " |
405 | 0 | : "TextureHost: "); |
406 | 0 | DumpTextureHost(aStream, img.mTextureHost); |
407 | 0 | aStream << (aDumpHtml ? " </li></ul> " : " "); |
408 | 0 | } |
409 | 0 | } |
410 | | |
411 | | already_AddRefed<gfx::DataSourceSurface> |
412 | | ImageHost::GetAsSurface() |
413 | 0 | { |
414 | 0 | const TimedImage* img = ChooseImage(); |
415 | 0 | if (img) { |
416 | 0 | return img->mTextureHost->GetAsSurface(); |
417 | 0 | } |
418 | 0 | return nullptr; |
419 | 0 | } |
420 | | |
421 | | bool |
422 | | ImageHost::Lock() |
423 | 0 | { |
424 | 0 | MOZ_ASSERT(!mLocked); |
425 | 0 | const TimedImage* img = ChooseImage(); |
426 | 0 | if (!img) { |
427 | 0 | return false; |
428 | 0 | } |
429 | 0 | |
430 | 0 | SetCurrentTextureHost(img->mTextureHost); |
431 | 0 |
|
432 | 0 | if (!mCurrentTextureHost->Lock()) { |
433 | 0 | return false; |
434 | 0 | } |
435 | 0 | mLocked = true; |
436 | 0 | return true; |
437 | 0 | } |
438 | | |
439 | | void |
440 | | ImageHost::Unlock() |
441 | 0 | { |
442 | 0 | MOZ_ASSERT(mLocked); |
443 | 0 |
|
444 | 0 | if (mCurrentTextureHost) { |
445 | 0 | mCurrentTextureHost->Unlock(); |
446 | 0 | } |
447 | 0 | mLocked = false; |
448 | 0 | } |
449 | | |
450 | | IntSize |
451 | | ImageHost::GetImageSize() |
452 | 0 | { |
453 | 0 | const TimedImage* img = ChooseImage(); |
454 | 0 | if (img) { |
455 | 0 | return IntSize(img->mPictureRect.Width(), img->mPictureRect.Height()); |
456 | 0 | } |
457 | 0 | return IntSize(); |
458 | 0 | } |
459 | | |
460 | | bool |
461 | | ImageHost::IsOpaque() |
462 | 0 | { |
463 | 0 | const TimedImage* img = ChooseImage(); |
464 | 0 | if (!img) { |
465 | 0 | return false; |
466 | 0 | } |
467 | 0 | |
468 | 0 | if (img->mPictureRect.Width() == 0 || |
469 | 0 | img->mPictureRect.Height() == 0 || |
470 | 0 | !img->mTextureHost) { |
471 | 0 | return false; |
472 | 0 | } |
473 | 0 | |
474 | 0 | gfx::SurfaceFormat format = img->mTextureHost->GetFormat(); |
475 | 0 | if (gfx::IsOpaque(format)) { |
476 | 0 | return true; |
477 | 0 | } |
478 | 0 | return false; |
479 | 0 | } |
480 | | |
481 | | already_AddRefed<TexturedEffect> |
482 | | ImageHost::GenEffect(const gfx::SamplingFilter aSamplingFilter) |
483 | 0 | { |
484 | 0 | const TimedImage* img = ChooseImage(); |
485 | 0 | if (!img) { |
486 | 0 | return nullptr; |
487 | 0 | } |
488 | 0 | SetCurrentTextureHost(img->mTextureHost); |
489 | 0 | if (!mCurrentTextureHost->BindTextureSource(mCurrentTextureSource)) { |
490 | 0 | return nullptr; |
491 | 0 | } |
492 | 0 | bool isAlphaPremultiplied = true; |
493 | 0 | if (mCurrentTextureHost->GetFlags() & TextureFlags::NON_PREMULTIPLIED) { |
494 | 0 | isAlphaPremultiplied = false; |
495 | 0 | } |
496 | 0 |
|
497 | 0 | return CreateTexturedEffect(mCurrentTextureHost, |
498 | 0 | mCurrentTextureSource, |
499 | 0 | aSamplingFilter, |
500 | 0 | isAlphaPremultiplied); |
501 | 0 | } |
502 | | |
503 | | } // namespace layers |
504 | | } // namespace mozilla |