/src/mozilla-central/gfx/layers/opengl/GLBlitTextureImageHelper.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ |
2 | | /* vim: set ts=8 sts=4 et sw=4 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 "GLBlitTextureImageHelper.h" |
8 | | #include "GLUploadHelpers.h" |
9 | | #include "DecomposeIntoNoRepeatTriangles.h" |
10 | | #include "GLContext.h" |
11 | | #include "GLTextureImage.h" |
12 | | #include "ScopedGLHelpers.h" |
13 | | #include "nsRect.h" |
14 | | #include "gfx2DGlue.h" |
15 | | #include "gfxUtils.h" |
16 | | #include "CompositorOGL.h" |
17 | | #include "mozilla/gfx/Point.h" |
18 | | |
19 | | using namespace mozilla::gl; |
20 | | |
21 | | namespace mozilla { |
22 | | namespace layers { |
23 | | |
24 | | GLBlitTextureImageHelper::GLBlitTextureImageHelper(CompositorOGL* aCompositor) |
25 | | : mCompositor(aCompositor) |
26 | | , mBlitProgram(0) |
27 | | , mBlitFramebuffer(0) |
28 | | |
29 | 0 | { |
30 | 0 | } |
31 | | |
32 | | GLBlitTextureImageHelper::~GLBlitTextureImageHelper() |
33 | 0 | { |
34 | 0 | GLContext *gl = mCompositor->gl(); |
35 | 0 | // Likely used by OGL Layers. |
36 | 0 | gl->fDeleteProgram(mBlitProgram); |
37 | 0 | gl->fDeleteFramebuffers(1, &mBlitFramebuffer); |
38 | 0 | } |
39 | | |
40 | | void |
41 | | GLBlitTextureImageHelper::BlitTextureImage(TextureImage *aSrc, const gfx::IntRect& aSrcRect, |
42 | | TextureImage *aDst, const gfx::IntRect& aDstRect) |
43 | 0 | { |
44 | 0 | GLContext *gl = mCompositor->gl(); |
45 | 0 |
|
46 | 0 | if (!aSrc || !aDst || aSrcRect.IsEmpty() || aDstRect.IsEmpty()) |
47 | 0 | return; |
48 | 0 | |
49 | 0 | int savedFb = 0; |
50 | 0 | gl->fGetIntegerv(LOCAL_GL_FRAMEBUFFER_BINDING, &savedFb); |
51 | 0 |
|
52 | 0 | ScopedGLState scopedScissorTestState(gl, LOCAL_GL_SCISSOR_TEST, false); |
53 | 0 | ScopedGLState scopedBlendState(gl, LOCAL_GL_BLEND, false); |
54 | 0 |
|
55 | 0 | // 2.0 means scale up by two |
56 | 0 | float blitScaleX = float(aDstRect.Width()) / float(aSrcRect.Width()); |
57 | 0 | float blitScaleY = float(aDstRect.Height()) / float(aSrcRect.Height()); |
58 | 0 |
|
59 | 0 | // We start iterating over all destination tiles |
60 | 0 | aDst->BeginBigImageIteration(); |
61 | 0 | do { |
62 | 0 | // calculate portion of the tile that is going to be painted to |
63 | 0 | gfx::IntRect dstSubRect; |
64 | 0 | gfx::IntRect dstTextureRect = aDst->GetTileRect(); |
65 | 0 | dstSubRect.IntersectRect(aDstRect, dstTextureRect); |
66 | 0 |
|
67 | 0 | // this tile is not part of the destination rectangle aDstRect |
68 | 0 | if (dstSubRect.IsEmpty()) |
69 | 0 | continue; |
70 | 0 | |
71 | 0 | // (*) transform the rect of this tile into the rectangle defined by aSrcRect... |
72 | 0 | gfx::IntRect dstInSrcRect(dstSubRect); |
73 | 0 | dstInSrcRect.MoveBy(-aDstRect.TopLeft()); |
74 | 0 | // ...which might be of different size, hence scale accordingly |
75 | 0 | dstInSrcRect.ScaleRoundOut(1.0f / blitScaleX, 1.0f / blitScaleY); |
76 | 0 | dstInSrcRect.MoveBy(aSrcRect.TopLeft()); |
77 | 0 |
|
78 | 0 | SetBlitFramebufferForDestTexture(aDst->GetTextureID()); |
79 | 0 | UseBlitProgram(); |
80 | 0 |
|
81 | 0 | aSrc->BeginBigImageIteration(); |
82 | 0 | // now iterate over all tiles in the source Image... |
83 | 0 | do { |
84 | 0 | // calculate portion of the source tile that is in the source rect |
85 | 0 | gfx::IntRect srcSubRect; |
86 | 0 | gfx::IntRect srcTextureRect = aSrc->GetTileRect(); |
87 | 0 | srcSubRect.IntersectRect(aSrcRect, srcTextureRect); |
88 | 0 |
|
89 | 0 | // this tile is not part of the source rect |
90 | 0 | if (srcSubRect.IsEmpty()) { |
91 | 0 | continue; |
92 | 0 | } |
93 | 0 | // calculate intersection of source rect with destination rect |
94 | 0 | srcSubRect.IntersectRect(srcSubRect, dstInSrcRect); |
95 | 0 | // this tile does not overlap the current destination tile |
96 | 0 | if (srcSubRect.IsEmpty()) { |
97 | 0 | continue; |
98 | 0 | } |
99 | 0 | // We now have the intersection of |
100 | 0 | // the current source tile |
101 | 0 | // and the desired source rectangle |
102 | 0 | // and the destination tile |
103 | 0 | // and the desired destination rectange |
104 | 0 | // in destination space. |
105 | 0 | // We need to transform this back into destination space, inverting the transform from (*) |
106 | 0 | gfx::IntRect srcSubInDstRect(srcSubRect); |
107 | 0 | srcSubInDstRect.MoveBy(-aSrcRect.TopLeft()); |
108 | 0 | srcSubInDstRect.ScaleRoundOut(blitScaleX, blitScaleY); |
109 | 0 | srcSubInDstRect.MoveBy(aDstRect.TopLeft()); |
110 | 0 |
|
111 | 0 | // we transform these rectangles to be relative to the current src and dst tiles, respectively |
112 | 0 | gfx::IntSize srcSize = srcTextureRect.Size(); |
113 | 0 | gfx::IntSize dstSize = dstTextureRect.Size(); |
114 | 0 | srcSubRect.MoveBy(-srcTextureRect.X(), -srcTextureRect.Y()); |
115 | 0 | srcSubInDstRect.MoveBy(-dstTextureRect.X(), -dstTextureRect.Y()); |
116 | 0 |
|
117 | 0 | float dx0 = 2.0f * float(srcSubInDstRect.X()) / float(dstSize.width) - 1.0f; |
118 | 0 | float dy0 = 2.0f * float(srcSubInDstRect.Y()) / float(dstSize.height) - 1.0f; |
119 | 0 | float dx1 = 2.0f * float(srcSubInDstRect.XMost()) / float(dstSize.width) - 1.0f; |
120 | 0 | float dy1 = 2.0f * float(srcSubInDstRect.YMost()) / float(dstSize.height) - 1.0f; |
121 | 0 | ScopedViewportRect autoViewportRect(gl, 0, 0, dstSize.width, dstSize.height); |
122 | 0 |
|
123 | 0 | RectTriangles rects; |
124 | 0 |
|
125 | 0 | gfx::IntSize realTexSize = srcSize; |
126 | 0 | if (!CanUploadNonPowerOfTwo(gl)) { |
127 | 0 | realTexSize = gfx::IntSize(RoundUpPow2(srcSize.width), |
128 | 0 | RoundUpPow2(srcSize.height)); |
129 | 0 | } |
130 | 0 |
|
131 | 0 | if (aSrc->GetWrapMode() == LOCAL_GL_REPEAT) { |
132 | 0 | rects.addRect(/* dest rectangle */ |
133 | 0 | dx0, dy0, dx1, dy1, |
134 | 0 | /* tex coords */ |
135 | 0 | srcSubRect.X() / float(realTexSize.width), |
136 | 0 | srcSubRect.Y() / float(realTexSize.height), |
137 | 0 | srcSubRect.XMost() / float(realTexSize.width), |
138 | 0 | srcSubRect.YMost() / float(realTexSize.height)); |
139 | 0 | } else { |
140 | 0 | DecomposeIntoNoRepeatTriangles(srcSubRect, realTexSize, rects); |
141 | 0 |
|
142 | 0 | // now put the coords into the d[xy]0 .. d[xy]1 coordinate space |
143 | 0 | // from the 0..1 that it comes out of decompose |
144 | 0 | InfallibleTArray<RectTriangles::coord>& coords = rects.vertCoords(); |
145 | 0 |
|
146 | 0 | for (unsigned int i = 0; i < coords.Length(); ++i) { |
147 | 0 | coords[i].x = (coords[i].x * (dx1 - dx0)) + dx0; |
148 | 0 | coords[i].y = (coords[i].y * (dy1 - dy0)) + dy0; |
149 | 0 | } |
150 | 0 | } |
151 | 0 |
|
152 | 0 | ScopedBindTextureUnit autoTexUnit(gl, LOCAL_GL_TEXTURE0); |
153 | 0 | ScopedBindTexture autoTex(gl, aSrc->GetTextureID()); |
154 | 0 | ScopedVertexAttribPointer autoAttrib0(gl, 0, 2, LOCAL_GL_FLOAT, LOCAL_GL_FALSE, 0, 0, rects.vertCoords().Elements()); |
155 | 0 | ScopedVertexAttribPointer autoAttrib1(gl, 1, 2, LOCAL_GL_FLOAT, LOCAL_GL_FALSE, 0, 0, rects.texCoords().Elements()); |
156 | 0 |
|
157 | 0 | gl->fDrawArrays(LOCAL_GL_TRIANGLES, 0, rects.elements()); |
158 | 0 |
|
159 | 0 | } while (aSrc->NextTile()); |
160 | 0 | } while (aDst->NextTile()); |
161 | 0 |
|
162 | 0 | // unbind the previous texture from the framebuffer |
163 | 0 | SetBlitFramebufferForDestTexture(0); |
164 | 0 |
|
165 | 0 | gl->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, savedFb); |
166 | 0 | } |
167 | | |
168 | | void |
169 | | GLBlitTextureImageHelper::SetBlitFramebufferForDestTexture(GLuint aTexture) |
170 | 0 | { |
171 | 0 | GLContext *gl = mCompositor->gl(); |
172 | 0 | if (!mBlitFramebuffer) { |
173 | 0 | gl->fGenFramebuffers(1, &mBlitFramebuffer); |
174 | 0 | } |
175 | 0 |
|
176 | 0 | gl->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, mBlitFramebuffer); |
177 | 0 | gl->fFramebufferTexture2D(LOCAL_GL_FRAMEBUFFER, |
178 | 0 | LOCAL_GL_COLOR_ATTACHMENT0, |
179 | 0 | LOCAL_GL_TEXTURE_2D, |
180 | 0 | aTexture, |
181 | 0 | 0); |
182 | 0 |
|
183 | 0 | GLenum result = gl->fCheckFramebufferStatus(LOCAL_GL_FRAMEBUFFER); |
184 | 0 | if (aTexture && (result != LOCAL_GL_FRAMEBUFFER_COMPLETE)) { |
185 | 0 | nsAutoCString msg; |
186 | 0 | msg.AppendLiteral("Framebuffer not complete -- error 0x"); |
187 | 0 | msg.AppendInt(result, 16); |
188 | 0 | // Note: if you are hitting this, it is likely that |
189 | 0 | // your texture is not texture complete -- that is, you |
190 | 0 | // allocated a texture name, but didn't actually define its |
191 | 0 | // size via a call to TexImage2D. |
192 | 0 | MOZ_CRASH_UNSAFE_OOL(msg.get()); |
193 | 0 | } |
194 | 0 | } |
195 | | |
196 | | void |
197 | | GLBlitTextureImageHelper::UseBlitProgram() |
198 | 0 | { |
199 | 0 | // XXX: GLBlitTextureImageHelper doesn't use ShaderProgramOGL |
200 | 0 | // so we need to Reset the program |
201 | 0 | mCompositor->ResetProgram(); |
202 | 0 |
|
203 | 0 | GLContext *gl = mCompositor->gl(); |
204 | 0 | if (mBlitProgram) { |
205 | 0 | gl->fUseProgram(mBlitProgram); |
206 | 0 | return; |
207 | 0 | } |
208 | 0 | |
209 | 0 | mBlitProgram = gl->fCreateProgram(); |
210 | 0 |
|
211 | 0 | GLuint shaders[2]; |
212 | 0 | shaders[0] = gl->fCreateShader(LOCAL_GL_VERTEX_SHADER); |
213 | 0 | shaders[1] = gl->fCreateShader(LOCAL_GL_FRAGMENT_SHADER); |
214 | 0 |
|
215 | 0 | const char *blitVSSrc = |
216 | 0 | "attribute vec2 aVertex;" |
217 | 0 | "attribute vec2 aTexCoord;" |
218 | 0 | "varying vec2 vTexCoord;" |
219 | 0 | "void main() {" |
220 | 0 | " vTexCoord = aTexCoord;" |
221 | 0 | " gl_Position = vec4(aVertex, 0.0, 1.0);" |
222 | 0 | "}"; |
223 | 0 | const char *blitFSSrc = "#ifdef GL_ES\nprecision mediump float;\n#endif\n" |
224 | 0 | "uniform sampler2D uSrcTexture;" |
225 | 0 | "varying vec2 vTexCoord;" |
226 | 0 | "void main() {" |
227 | 0 | " gl_FragColor = texture2D(uSrcTexture, vTexCoord);" |
228 | 0 | "}"; |
229 | 0 |
|
230 | 0 | gl->fShaderSource(shaders[0], 1, (const GLchar**) &blitVSSrc, nullptr); |
231 | 0 | gl->fShaderSource(shaders[1], 1, (const GLchar**) &blitFSSrc, nullptr); |
232 | 0 |
|
233 | 0 | for (int i = 0; i < 2; ++i) { |
234 | 0 | GLint success, len = 0; |
235 | 0 |
|
236 | 0 | gl->fCompileShader(shaders[i]); |
237 | 0 | gl->fGetShaderiv(shaders[i], LOCAL_GL_COMPILE_STATUS, &success); |
238 | 0 | NS_ASSERTION(success, "Shader compilation failed!"); |
239 | 0 |
|
240 | 0 | if (!success) { |
241 | 0 | nsAutoCString log; |
242 | 0 | gl->fGetShaderiv(shaders[i], LOCAL_GL_INFO_LOG_LENGTH, (GLint*) &len); |
243 | 0 | log.SetLength(len); |
244 | 0 | gl->fGetShaderInfoLog(shaders[i], len, (GLint*) &len, (char*) log.BeginWriting()); |
245 | 0 |
|
246 | 0 | printf_stderr("Shader %d compilation failed:\n%s\n", i, log.get()); |
247 | 0 | return; |
248 | 0 | } |
249 | 0 |
|
250 | 0 | gl->fAttachShader(mBlitProgram, shaders[i]); |
251 | 0 | gl->fDeleteShader(shaders[i]); |
252 | 0 | } |
253 | 0 |
|
254 | 0 | gl->fBindAttribLocation(mBlitProgram, 0, "aVertex"); |
255 | 0 | gl->fBindAttribLocation(mBlitProgram, 1, "aTexCoord"); |
256 | 0 |
|
257 | 0 | gl->fLinkProgram(mBlitProgram); |
258 | 0 |
|
259 | 0 | GLint success, len = 0; |
260 | 0 | gl->fGetProgramiv(mBlitProgram, LOCAL_GL_LINK_STATUS, &success); |
261 | 0 | NS_ASSERTION(success, "Shader linking failed!"); |
262 | 0 |
|
263 | 0 | if (!success) { |
264 | 0 | nsAutoCString log; |
265 | 0 | gl->fGetProgramiv(mBlitProgram, LOCAL_GL_INFO_LOG_LENGTH, (GLint*) &len); |
266 | 0 | log.SetLength(len); |
267 | 0 | gl->fGetProgramInfoLog(mBlitProgram, len, (GLint*) &len, (char*) log.BeginWriting()); |
268 | 0 |
|
269 | 0 | printf_stderr("Program linking failed:\n%s\n", log.get()); |
270 | 0 | return; |
271 | 0 | } |
272 | 0 |
|
273 | 0 | gl->fUseProgram(mBlitProgram); |
274 | 0 | gl->fUniform1i(gl->fGetUniformLocation(mBlitProgram, "uSrcTexture"), 0); |
275 | 0 | } |
276 | | |
277 | | } // namespace layers |
278 | | } // namespace mozilla |