blob: 18c2d94f80797cfe6ba2d84e0a68ab3e424aac1a [file] [log] [blame]
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +00001//
2// Copyright (c) 2002-2010 The ANGLE Project Authors. All rights reserved.
3// Use of this source code is governed by a BSD-style license that can be
4// found in the LICENSE file.
5//
6
7// Framebuffer.cpp: Implements the gl::Framebuffer class. Implements GL framebuffer
8// objects and related functionality. [OpenGL ES 2.0.24] section 4.4 page 105.
9
daniel@transgaming.combbf56f72010-04-20 18:52:13 +000010#include "libGLESv2/Framebuffer.h"
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +000011
daniel@transgaming.combbf56f72010-04-20 18:52:13 +000012#include "libGLESv2/main.h"
13#include "libGLESv2/Renderbuffer.h"
14#include "libGLESv2/Texture.h"
daniel@transgaming.com93a81472010-04-20 18:52:58 +000015#include "libGLESv2/utilities.h"
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +000016
17namespace gl
18{
daniel@transgaming.com9ecb9f92010-07-28 19:21:12 +000019
apatrick@chromium.orgff8bdfb2010-09-15 17:27:49 +000020Framebuffer::Framebuffer()
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +000021{
22 mColorbufferType = GL_NONE;
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +000023 mDepthbufferType = GL_NONE;
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +000024 mStencilbufferType = GL_NONE;
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +000025}
26
27Framebuffer::~Framebuffer()
28{
daniel@transgaming.com9ecb9f92010-07-28 19:21:12 +000029 mColorbufferPointer.set(NULL);
30 mDepthbufferPointer.set(NULL);
31 mStencilbufferPointer.set(NULL);
32}
33
34Renderbuffer *Framebuffer::lookupRenderbuffer(GLenum type, GLuint handle) const
35{
36 gl::Context *context = gl::getContext();
37 Renderbuffer *buffer = NULL;
38
39 if (type == GL_NONE)
40 {
41 buffer = NULL;
42 }
43 else if (type == GL_RENDERBUFFER)
44 {
45 buffer = context->getRenderbuffer(handle);
46 }
apatrick@chromium.org551022e2012-01-23 19:56:54 +000047 else if (IsInternalTextureTarget(type))
daniel@transgaming.com9ecb9f92010-07-28 19:21:12 +000048 {
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +000049 buffer = context->getTexture(handle)->getRenderbuffer(type);
daniel@transgaming.com9ecb9f92010-07-28 19:21:12 +000050 }
51 else
52 {
53 UNREACHABLE();
54 }
55
56 return buffer;
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +000057}
58
59void Framebuffer::setColorbuffer(GLenum type, GLuint colorbuffer)
60{
daniel@transgaming.com2fa45512011-10-04 18:43:18 +000061 mColorbufferType = (colorbuffer != 0) ? type : GL_NONE;
daniel@transgaming.com9ecb9f92010-07-28 19:21:12 +000062 mColorbufferPointer.set(lookupRenderbuffer(type, colorbuffer));
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +000063}
64
65void Framebuffer::setDepthbuffer(GLenum type, GLuint depthbuffer)
66{
daniel@transgaming.com2fa45512011-10-04 18:43:18 +000067 mDepthbufferType = (depthbuffer != 0) ? type : GL_NONE;
daniel@transgaming.com9ecb9f92010-07-28 19:21:12 +000068 mDepthbufferPointer.set(lookupRenderbuffer(type, depthbuffer));
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +000069}
70
71void Framebuffer::setStencilbuffer(GLenum type, GLuint stencilbuffer)
72{
daniel@transgaming.com2fa45512011-10-04 18:43:18 +000073 mStencilbufferType = (stencilbuffer != 0) ? type : GL_NONE;
daniel@transgaming.com9ecb9f92010-07-28 19:21:12 +000074 mStencilbufferPointer.set(lookupRenderbuffer(type, stencilbuffer));
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +000075}
76
77void Framebuffer::detachTexture(GLuint texture)
78{
apatrick@chromium.org551022e2012-01-23 19:56:54 +000079 if (mColorbufferPointer.id() == texture && IsInternalTextureTarget(mColorbufferType))
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +000080 {
81 mColorbufferType = GL_NONE;
daniel@transgaming.com9ecb9f92010-07-28 19:21:12 +000082 mColorbufferPointer.set(NULL);
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +000083 }
daniel@transgaming.comfbc09532010-04-26 15:33:41 +000084
apatrick@chromium.org551022e2012-01-23 19:56:54 +000085 if (mDepthbufferPointer.id() == texture && IsInternalTextureTarget(mDepthbufferType))
daniel@transgaming.comfbc09532010-04-26 15:33:41 +000086 {
87 mDepthbufferType = GL_NONE;
daniel@transgaming.com9ecb9f92010-07-28 19:21:12 +000088 mDepthbufferPointer.set(NULL);
daniel@transgaming.comfbc09532010-04-26 15:33:41 +000089 }
90
apatrick@chromium.org551022e2012-01-23 19:56:54 +000091 if (mStencilbufferPointer.id() == texture && IsInternalTextureTarget(mStencilbufferType))
daniel@transgaming.comfbc09532010-04-26 15:33:41 +000092 {
93 mStencilbufferType = GL_NONE;
daniel@transgaming.com9ecb9f92010-07-28 19:21:12 +000094 mStencilbufferPointer.set(NULL);
daniel@transgaming.comfbc09532010-04-26 15:33:41 +000095 }
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +000096}
97
98void Framebuffer::detachRenderbuffer(GLuint renderbuffer)
99{
daniel@transgaming.com9ecb9f92010-07-28 19:21:12 +0000100 if (mColorbufferPointer.id() == renderbuffer && mColorbufferType == GL_RENDERBUFFER)
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000101 {
102 mColorbufferType = GL_NONE;
daniel@transgaming.com9ecb9f92010-07-28 19:21:12 +0000103 mColorbufferPointer.set(NULL);
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000104 }
105
daniel@transgaming.com9ecb9f92010-07-28 19:21:12 +0000106 if (mDepthbufferPointer.id() == renderbuffer && mDepthbufferType == GL_RENDERBUFFER)
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000107 {
108 mDepthbufferType = GL_NONE;
daniel@transgaming.com9ecb9f92010-07-28 19:21:12 +0000109 mDepthbufferPointer.set(NULL);
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000110 }
111
daniel@transgaming.com9ecb9f92010-07-28 19:21:12 +0000112 if (mStencilbufferPointer.id() == renderbuffer && mStencilbufferType == GL_RENDERBUFFER)
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000113 {
114 mStencilbufferType = GL_NONE;
daniel@transgaming.com9ecb9f92010-07-28 19:21:12 +0000115 mStencilbufferPointer.set(NULL);
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000116 }
117}
118
daniel@transgaming.com092bd482010-05-12 03:39:36 +0000119unsigned int Framebuffer::getRenderTargetSerial()
120{
daniel@transgaming.com9ecb9f92010-07-28 19:21:12 +0000121 Renderbuffer *colorbuffer = mColorbufferPointer.get();
daniel@transgaming.com092bd482010-05-12 03:39:36 +0000122
123 if (colorbuffer)
124 {
125 return colorbuffer->getSerial();
126 }
127
128 return 0;
129}
130
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000131IDirect3DSurface9 *Framebuffer::getRenderTarget()
132{
daniel@transgaming.com9ecb9f92010-07-28 19:21:12 +0000133 Renderbuffer *colorbuffer = mColorbufferPointer.get();
daniel@transgaming.comfab5a1a2010-03-11 19:22:30 +0000134
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000135 if (colorbuffer)
136 {
137 return colorbuffer->getRenderTarget();
138 }
139
140 return NULL;
141}
142
daniel@transgaming.com4cbc5902010-08-24 19:20:26 +0000143IDirect3DSurface9 *Framebuffer::getDepthStencil()
144{
145 Renderbuffer *depthstencilbuffer = mDepthbufferPointer.get();
146
147 if (!depthstencilbuffer)
148 {
149 depthstencilbuffer = mStencilbufferPointer.get();
150 }
151
152 if (depthstencilbuffer)
153 {
154 return depthstencilbuffer->getDepthStencil();
155 }
156
157 return NULL;
158}
159
daniel@transgaming.com339ae702010-05-12 03:40:20 +0000160unsigned int Framebuffer::getDepthbufferSerial()
161{
daniel@transgaming.com9ecb9f92010-07-28 19:21:12 +0000162 Renderbuffer *depthbuffer = mDepthbufferPointer.get();
daniel@transgaming.com339ae702010-05-12 03:40:20 +0000163
164 if (depthbuffer)
165 {
166 return depthbuffer->getSerial();
167 }
168
169 return 0;
170}
171
daniel@transgaming.com4cbc5902010-08-24 19:20:26 +0000172unsigned int Framebuffer::getStencilbufferSerial()
173{
174 Renderbuffer *stencilbuffer = mStencilbufferPointer.get();
175
176 if (stencilbuffer)
177 {
178 return stencilbuffer->getSerial();
179 }
180
181 return 0;
182}
183
daniel@transgaming.comd14558a2011-11-09 17:46:18 +0000184Renderbuffer *Framebuffer::getColorbuffer()
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000185{
daniel@transgaming.comd14558a2011-11-09 17:46:18 +0000186 return mColorbufferPointer.get();
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000187}
188
daniel@transgaming.comd14558a2011-11-09 17:46:18 +0000189Renderbuffer *Framebuffer::getDepthbuffer()
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000190{
daniel@transgaming.comd14558a2011-11-09 17:46:18 +0000191 return mDepthbufferPointer.get();
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000192}
193
daniel@transgaming.comd14558a2011-11-09 17:46:18 +0000194Renderbuffer *Framebuffer::getStencilbuffer()
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000195{
daniel@transgaming.comd14558a2011-11-09 17:46:18 +0000196 return mStencilbufferPointer.get();
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000197}
198
daniel@transgaming.comc46c9c02010-04-23 18:34:55 +0000199GLenum Framebuffer::getColorbufferType()
200{
201 return mColorbufferType;
202}
203
204GLenum Framebuffer::getDepthbufferType()
205{
206 return mDepthbufferType;
207}
208
209GLenum Framebuffer::getStencilbufferType()
210{
211 return mStencilbufferType;
212}
213
214GLuint Framebuffer::getColorbufferHandle()
215{
daniel@transgaming.com9ecb9f92010-07-28 19:21:12 +0000216 return mColorbufferPointer.id();
daniel@transgaming.comc46c9c02010-04-23 18:34:55 +0000217}
218
219GLuint Framebuffer::getDepthbufferHandle()
220{
daniel@transgaming.com9ecb9f92010-07-28 19:21:12 +0000221 return mDepthbufferPointer.id();
daniel@transgaming.comc46c9c02010-04-23 18:34:55 +0000222}
223
224GLuint Framebuffer::getStencilbufferHandle()
225{
daniel@transgaming.com9ecb9f92010-07-28 19:21:12 +0000226 return mStencilbufferPointer.id();
daniel@transgaming.comc46c9c02010-04-23 18:34:55 +0000227}
228
daniel@transgaming.coma27ff1e2010-08-24 19:20:11 +0000229bool Framebuffer::hasStencil()
230{
231 if (mStencilbufferType != GL_NONE)
232 {
daniel@transgaming.comd14558a2011-11-09 17:46:18 +0000233 Renderbuffer *stencilbufferObject = getStencilbuffer();
daniel@transgaming.coma27ff1e2010-08-24 19:20:11 +0000234
235 if (stencilbufferObject)
236 {
237 return stencilbufferObject->getStencilSize() > 0;
238 }
239 }
240
241 return false;
242}
243
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000244GLenum Framebuffer::completeness()
245{
daniel@transgaming.comd885df02012-05-31 01:14:36 +0000246 gl::Context *context = gl::getContext();
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000247 int width = 0;
248 int height = 0;
daniel@transgaming.com1f135d82010-08-24 19:20:36 +0000249 int samples = -1;
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000250
daniel@transgaming.com9ecb9f92010-07-28 19:21:12 +0000251 if (mColorbufferType != GL_NONE)
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000252 {
daniel@transgaming.comd14558a2011-11-09 17:46:18 +0000253 Renderbuffer *colorbuffer = getColorbuffer();
daniel@transgaming.com9ecb9f92010-07-28 19:21:12 +0000254
255 if (!colorbuffer)
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000256 {
daniel@transgaming.com9ecb9f92010-07-28 19:21:12 +0000257 return GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT;
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000258 }
259
daniel@transgaming.com9ecb9f92010-07-28 19:21:12 +0000260 if (colorbuffer->getWidth() == 0 || colorbuffer->getHeight() == 0)
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000261 {
daniel@transgaming.com9ecb9f92010-07-28 19:21:12 +0000262 return GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT;
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000263 }
264
daniel@transgaming.comedc19182010-10-15 17:57:55 +0000265 if (mColorbufferType == GL_RENDERBUFFER)
266 {
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +0000267 if (!gl::IsColorRenderable(colorbuffer->getInternalFormat()))
daniel@transgaming.comedc19182010-10-15 17:57:55 +0000268 {
269 return GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT;
270 }
271 }
apatrick@chromium.org551022e2012-01-23 19:56:54 +0000272 else if (IsInternalTextureTarget(mColorbufferType))
daniel@transgaming.com01868132010-08-24 19:21:17 +0000273 {
daniel@transgaming.comd885df02012-05-31 01:14:36 +0000274 GLenum internalformat = colorbuffer->getInternalFormat();
275 D3DFORMAT d3dformat = colorbuffer->getD3DFormat();
276
277 if (IsCompressed(internalformat) ||
278 internalformat == GL_LUMINANCE ||
279 internalformat == GL_LUMINANCE_ALPHA)
daniel@transgaming.com01868132010-08-24 19:21:17 +0000280 {
281 return GL_FRAMEBUFFER_UNSUPPORTED;
282 }
daniel@transgaming.com1297d922010-09-01 15:47:47 +0000283
daniel@transgaming.comd885df02012-05-31 01:14:36 +0000284 if ((dx2es::IsFloat32Format(d3dformat) && !context->supportsFloat32RenderableTextures()) ||
285 (dx2es::IsFloat16Format(d3dformat) && !context->supportsFloat16RenderableTextures()))
daniel@transgaming.comb6b2e672010-10-15 17:57:47 +0000286 {
287 return GL_FRAMEBUFFER_UNSUPPORTED;
288 }
daniel@transgaming.com01868132010-08-24 19:21:17 +0000289 }
daniel@transgaming.comd885df02012-05-31 01:14:36 +0000290 else
291 {
292 UNREACHABLE();
293 return GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT;
294 }
daniel@transgaming.com01868132010-08-24 19:21:17 +0000295
daniel@transgaming.com9ecb9f92010-07-28 19:21:12 +0000296 width = colorbuffer->getWidth();
297 height = colorbuffer->getHeight();
daniel@transgaming.com1f135d82010-08-24 19:20:36 +0000298 samples = colorbuffer->getSamples();
daniel@transgaming.com9ecb9f92010-07-28 19:21:12 +0000299 }
300 else
301 {
302 return GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT;
303 }
304
daniel@transgaming.comd14558a2011-11-09 17:46:18 +0000305 Renderbuffer *depthbuffer = NULL;
306 Renderbuffer *stencilbuffer = NULL;
daniel@transgaming.com9ecb9f92010-07-28 19:21:12 +0000307
308 if (mDepthbufferType != GL_NONE)
309 {
daniel@transgaming.comedc19182010-10-15 17:57:55 +0000310 if (mDepthbufferType != GL_RENDERBUFFER)
311 {
312 return GL_FRAMEBUFFER_UNSUPPORTED; // Requires GL_OES_depth_texture
313 }
314
daniel@transgaming.com9ecb9f92010-07-28 19:21:12 +0000315 depthbuffer = getDepthbuffer();
316
317 if (!depthbuffer)
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000318 {
daniel@transgaming.com9ecb9f92010-07-28 19:21:12 +0000319 return GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT;
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000320 }
321
daniel@transgaming.com9ecb9f92010-07-28 19:21:12 +0000322 if (depthbuffer->getWidth() == 0 || depthbuffer->getHeight() == 0)
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000323 {
daniel@transgaming.com9ecb9f92010-07-28 19:21:12 +0000324 return GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT;
325 }
326
327 if (width == 0)
328 {
329 width = depthbuffer->getWidth();
330 height = depthbuffer->getHeight();
331 }
332 else if (width != depthbuffer->getWidth() || height != depthbuffer->getHeight())
333 {
334 return GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS;
335 }
daniel@transgaming.com1f135d82010-08-24 19:20:36 +0000336
337 if (samples == -1)
338 {
339 samples = depthbuffer->getSamples();
340 }
341 else if (samples != depthbuffer->getSamples())
342 {
343 return GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE_ANGLE;
344 }
daniel@transgaming.com9ecb9f92010-07-28 19:21:12 +0000345 }
346
347 if (mStencilbufferType != GL_NONE)
348 {
daniel@transgaming.comedc19182010-10-15 17:57:55 +0000349 if (mStencilbufferType != GL_RENDERBUFFER)
350 {
351 return GL_FRAMEBUFFER_UNSUPPORTED;
352 }
353
daniel@transgaming.com9ecb9f92010-07-28 19:21:12 +0000354 stencilbuffer = getStencilbuffer();
355
356 if (!stencilbuffer)
357 {
358 return GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT;
359 }
360
361 if (stencilbuffer->getWidth() == 0 || stencilbuffer->getHeight() == 0)
362 {
363 return GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT;
364 }
365
366 if (width == 0)
367 {
368 width = stencilbuffer->getWidth();
369 height = stencilbuffer->getHeight();
370 }
371 else if (width != stencilbuffer->getWidth() || height != stencilbuffer->getHeight())
372 {
373 return GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS;
374 }
daniel@transgaming.com1f135d82010-08-24 19:20:36 +0000375
376 if (samples == -1)
377 {
378 samples = stencilbuffer->getSamples();
379 }
380 else if (samples != stencilbuffer->getSamples())
381 {
382 return GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE_ANGLE;
383 }
daniel@transgaming.com9ecb9f92010-07-28 19:21:12 +0000384 }
385
386 if (mDepthbufferType == GL_RENDERBUFFER && mStencilbufferType == GL_RENDERBUFFER)
387 {
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +0000388 if (depthbuffer->getInternalFormat() != GL_DEPTH24_STENCIL8_OES ||
389 stencilbuffer->getInternalFormat() != GL_DEPTH24_STENCIL8_OES ||
daniel@transgaming.com9ecb9f92010-07-28 19:21:12 +0000390 depthbuffer->getSerial() != stencilbuffer->getSerial())
391 {
392 return GL_FRAMEBUFFER_UNSUPPORTED;
daniel@transgaming.comcdacc8e2010-07-28 19:20:50 +0000393 }
394 }
395
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000396 return GL_FRAMEBUFFER_COMPLETE;
397}
daniel@transgaming.com9ecb9f92010-07-28 19:21:12 +0000398
daniel@transgaming.comd14558a2011-11-09 17:46:18 +0000399DefaultFramebuffer::DefaultFramebuffer(Colorbuffer *colorbuffer, DepthStencilbuffer *depthStencil)
daniel@transgaming.com9ecb9f92010-07-28 19:21:12 +0000400{
daniel@transgaming.comd14558a2011-11-09 17:46:18 +0000401 mColorbufferPointer.set(new Renderbuffer(0, colorbuffer));
daniel@transgaming.com9ecb9f92010-07-28 19:21:12 +0000402
403 Renderbuffer *depthStencilRenderbuffer = new Renderbuffer(0, depthStencil);
404 mDepthbufferPointer.set(depthStencilRenderbuffer);
405 mStencilbufferPointer.set(depthStencilRenderbuffer);
daniel@transgaming.comd14558a2011-11-09 17:46:18 +0000406
407 mColorbufferType = GL_RENDERBUFFER;
408 mDepthbufferType = (depthStencilRenderbuffer->getDepthSize() != 0) ? GL_RENDERBUFFER : GL_NONE;
409 mStencilbufferType = (depthStencilRenderbuffer->getStencilSize() != 0) ? GL_RENDERBUFFER : GL_NONE;
daniel@transgaming.com9ecb9f92010-07-28 19:21:12 +0000410}
411
daniel@transgaming.com1f135d82010-08-24 19:20:36 +0000412int Framebuffer::getSamples()
413{
414 if (completeness() == GL_FRAMEBUFFER_COMPLETE)
415 {
416 return getColorbuffer()->getSamples();
417 }
418 else
419 {
420 return 0;
421 }
422}
423
daniel@transgaming.com9ecb9f92010-07-28 19:21:12 +0000424GLenum DefaultFramebuffer::completeness()
425{
daniel@transgaming.com73a5db62010-10-15 17:58:13 +0000426 // The default framebuffer should always be complete
427 ASSERT(Framebuffer::completeness() == GL_FRAMEBUFFER_COMPLETE);
428
daniel@transgaming.com9ecb9f92010-07-28 19:21:12 +0000429 return GL_FRAMEBUFFER_COMPLETE;
430}
431
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000432}