mesa: Fix some signed-unsigned comparison warnings
[mesa.git] / src / mesa / main / clear.c
1 /*
2 * Mesa 3-D graphics library
3 *
4 * Copyright (C) 1999-2007 Brian Paul All Rights Reserved.
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a
7 * copy of this software and associated documentation files (the "Software"),
8 * to deal in the Software without restriction, including without limitation
9 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
10 * and/or sell copies of the Software, and to permit persons to whom the
11 * Software is furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included
14 * in all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
17 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
20 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
21 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22 * OTHER DEALINGS IN THE SOFTWARE.
23 */
24
25
26 /**
27 * \file clear.c
28 * glClearColor, glClearIndex, glClear() functions.
29 */
30
31
32
33 #include "glheader.h"
34 #include "clear.h"
35 #include "context.h"
36 #include "enums.h"
37 #include "macros.h"
38 #include "mtypes.h"
39 #include "state.h"
40
41
42
43 void GLAPIENTRY
44 _mesa_ClearIndex( GLfloat c )
45 {
46 GET_CURRENT_CONTEXT(ctx);
47
48 ctx->Color.ClearIndex = (GLuint) c;
49 }
50
51
52 /**
53 * Specify the clear values for the color buffers.
54 *
55 * \param red red color component.
56 * \param green green color component.
57 * \param blue blue color component.
58 * \param alpha alpha component.
59 *
60 * \sa glClearColor().
61 */
62 void GLAPIENTRY
63 _mesa_ClearColor( GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha )
64 {
65 GET_CURRENT_CONTEXT(ctx);
66
67 ctx->Color.ClearColor.f[0] = red;
68 ctx->Color.ClearColor.f[1] = green;
69 ctx->Color.ClearColor.f[2] = blue;
70 ctx->Color.ClearColor.f[3] = alpha;
71 }
72
73
74 /**
75 * GL_EXT_texture_integer
76 */
77 void GLAPIENTRY
78 _mesa_ClearColorIiEXT(GLint r, GLint g, GLint b, GLint a)
79 {
80 GET_CURRENT_CONTEXT(ctx);
81
82 ctx->Color.ClearColor.i[0] = r;
83 ctx->Color.ClearColor.i[1] = g;
84 ctx->Color.ClearColor.i[2] = b;
85 ctx->Color.ClearColor.i[3] = a;
86 }
87
88
89 /**
90 * GL_EXT_texture_integer
91 */
92 void GLAPIENTRY
93 _mesa_ClearColorIuiEXT(GLuint r, GLuint g, GLuint b, GLuint a)
94 {
95 GET_CURRENT_CONTEXT(ctx);
96
97 ctx->Color.ClearColor.ui[0] = r;
98 ctx->Color.ClearColor.ui[1] = g;
99 ctx->Color.ClearColor.ui[2] = b;
100 ctx->Color.ClearColor.ui[3] = a;
101 }
102
103
104 /**
105 * Returns true if color writes are enabled for the given color attachment.
106 *
107 * Beyond checking ColorMask, this uses _mesa_format_has_color_component to
108 * ignore components that don't actually exist in the format (such as X in
109 * XRGB).
110 */
111 static bool
112 color_buffer_writes_enabled(const struct gl_context *ctx, unsigned idx)
113 {
114 struct gl_renderbuffer *rb = ctx->DrawBuffer->_ColorDrawBuffers[idx];
115 GLuint c;
116 GLubyte colorMask = 0;
117
118 if (rb) {
119 for (c = 0; c < 4; c++) {
120 if (_mesa_format_has_color_component(rb->Format, c))
121 colorMask |= ctx->Color.ColorMask[idx][c];
122 }
123 }
124
125 return colorMask != 0;
126 }
127
128
129 /**
130 * Clear buffers.
131 *
132 * \param mask bit-mask indicating the buffers to be cleared.
133 *
134 * Flushes the vertices and verifies the parameter.
135 * If __struct gl_contextRec::NewState is set then calls _mesa_update_state()
136 * to update gl_frame_buffer::_Xmin, etc. If the rasterization mode is set to
137 * GL_RENDER then requests the driver to clear the buffers, via the
138 * dd_function_table::Clear callback.
139 */
140 void GLAPIENTRY
141 _mesa_Clear( GLbitfield mask )
142 {
143 GET_CURRENT_CONTEXT(ctx);
144 FLUSH_VERTICES(ctx, 0);
145
146 FLUSH_CURRENT(ctx, 0);
147
148 if (MESA_VERBOSE & VERBOSE_API)
149 _mesa_debug(ctx, "glClear 0x%x\n", mask);
150
151 if (mask & ~(GL_COLOR_BUFFER_BIT |
152 GL_DEPTH_BUFFER_BIT |
153 GL_STENCIL_BUFFER_BIT |
154 GL_ACCUM_BUFFER_BIT)) {
155 /* invalid bit set */
156 _mesa_error( ctx, GL_INVALID_VALUE, "glClear(0x%x)", mask);
157 return;
158 }
159
160 /* Accumulation buffers were removed in core contexts, and they never
161 * existed in OpenGL ES.
162 */
163 if ((mask & GL_ACCUM_BUFFER_BIT) != 0
164 && (ctx->API == API_OPENGL_CORE || _mesa_is_gles(ctx))) {
165 _mesa_error( ctx, GL_INVALID_VALUE, "glClear(GL_ACCUM_BUFFER_BIT)");
166 return;
167 }
168
169 if (ctx->NewState) {
170 _mesa_update_state( ctx ); /* update _Xmin, etc */
171 }
172
173 if (ctx->DrawBuffer->_Status != GL_FRAMEBUFFER_COMPLETE_EXT) {
174 _mesa_error(ctx, GL_INVALID_FRAMEBUFFER_OPERATION_EXT,
175 "glClear(incomplete framebuffer)");
176 return;
177 }
178
179 if (ctx->RasterDiscard)
180 return;
181
182 if (ctx->RenderMode == GL_RENDER) {
183 GLbitfield bufferMask;
184
185 /* don't clear depth buffer if depth writing disabled */
186 if (!ctx->Depth.Mask)
187 mask &= ~GL_DEPTH_BUFFER_BIT;
188
189 /* Build the bitmask to send to device driver's Clear function.
190 * Note that the GL_COLOR_BUFFER_BIT flag will expand to 0, 1, 2 or 4
191 * of the BUFFER_BIT_FRONT/BACK_LEFT/RIGHT flags, or one of the
192 * BUFFER_BIT_COLORn flags.
193 */
194 bufferMask = 0;
195 if (mask & GL_COLOR_BUFFER_BIT) {
196 GLuint i;
197 for (i = 0; i < ctx->DrawBuffer->_NumColorDrawBuffers; i++) {
198 GLint buf = ctx->DrawBuffer->_ColorDrawBufferIndexes[i];
199
200 if (buf >= 0 && color_buffer_writes_enabled(ctx, i)) {
201 bufferMask |= 1 << buf;
202 }
203 }
204 }
205
206 if ((mask & GL_DEPTH_BUFFER_BIT)
207 && ctx->DrawBuffer->Visual.haveDepthBuffer) {
208 bufferMask |= BUFFER_BIT_DEPTH;
209 }
210
211 if ((mask & GL_STENCIL_BUFFER_BIT)
212 && ctx->DrawBuffer->Visual.haveStencilBuffer) {
213 bufferMask |= BUFFER_BIT_STENCIL;
214 }
215
216 if ((mask & GL_ACCUM_BUFFER_BIT)
217 && ctx->DrawBuffer->Visual.haveAccumBuffer) {
218 bufferMask |= BUFFER_BIT_ACCUM;
219 }
220
221 ASSERT(ctx->Driver.Clear);
222 ctx->Driver.Clear(ctx, bufferMask);
223 }
224 }
225
226
227 /** Returned by make_color_buffer_mask() for errors */
228 #define INVALID_MASK ~0x0U
229
230
231 /**
232 * Convert the glClearBuffer 'drawbuffer' parameter into a bitmask of
233 * BUFFER_BIT_x values.
234 * Return INVALID_MASK if the drawbuffer value is invalid.
235 */
236 static GLbitfield
237 make_color_buffer_mask(struct gl_context *ctx, GLint drawbuffer)
238 {
239 const struct gl_renderbuffer_attachment *att = ctx->DrawBuffer->Attachment;
240 GLbitfield mask = 0x0;
241
242 /* From the GL 4.0 specification:
243 * If buffer is COLOR, a particular draw buffer DRAW_BUFFERi is
244 * specified by passing i as the parameter drawbuffer, and value
245 * points to a four-element vector specifying the R, G, B, and A
246 * color to clear that draw buffer to. If the draw buffer is one
247 * of FRONT, BACK, LEFT, RIGHT, or FRONT_AND_BACK, identifying
248 * multiple buffers, each selected buffer is cleared to the same
249 * value.
250 *
251 * Note that "drawbuffer" and "draw buffer" have different meaning.
252 * "drawbuffer" specifies DRAW_BUFFERi, while "draw buffer" is what's
253 * assigned to DRAW_BUFFERi. It could be COLOR_ATTACHMENT0, FRONT, BACK,
254 * etc.
255 */
256 if (drawbuffer < 0 || drawbuffer >= (GLint)ctx->Const.MaxDrawBuffers) {
257 return INVALID_MASK;
258 }
259
260 switch (ctx->DrawBuffer->ColorDrawBuffer[drawbuffer]) {
261 case GL_FRONT:
262 if (att[BUFFER_FRONT_LEFT].Renderbuffer)
263 mask |= BUFFER_BIT_FRONT_LEFT;
264 if (att[BUFFER_FRONT_RIGHT].Renderbuffer)
265 mask |= BUFFER_BIT_FRONT_RIGHT;
266 break;
267 case GL_BACK:
268 if (att[BUFFER_BACK_LEFT].Renderbuffer)
269 mask |= BUFFER_BIT_BACK_LEFT;
270 if (att[BUFFER_BACK_RIGHT].Renderbuffer)
271 mask |= BUFFER_BIT_BACK_RIGHT;
272 break;
273 case GL_LEFT:
274 if (att[BUFFER_FRONT_LEFT].Renderbuffer)
275 mask |= BUFFER_BIT_FRONT_LEFT;
276 if (att[BUFFER_BACK_LEFT].Renderbuffer)
277 mask |= BUFFER_BIT_BACK_LEFT;
278 break;
279 case GL_RIGHT:
280 if (att[BUFFER_FRONT_RIGHT].Renderbuffer)
281 mask |= BUFFER_BIT_FRONT_RIGHT;
282 if (att[BUFFER_BACK_RIGHT].Renderbuffer)
283 mask |= BUFFER_BIT_BACK_RIGHT;
284 break;
285 case GL_FRONT_AND_BACK:
286 if (att[BUFFER_FRONT_LEFT].Renderbuffer)
287 mask |= BUFFER_BIT_FRONT_LEFT;
288 if (att[BUFFER_BACK_LEFT].Renderbuffer)
289 mask |= BUFFER_BIT_BACK_LEFT;
290 if (att[BUFFER_FRONT_RIGHT].Renderbuffer)
291 mask |= BUFFER_BIT_FRONT_RIGHT;
292 if (att[BUFFER_BACK_RIGHT].Renderbuffer)
293 mask |= BUFFER_BIT_BACK_RIGHT;
294 break;
295 default:
296 {
297 GLint buf = ctx->DrawBuffer->_ColorDrawBufferIndexes[drawbuffer];
298
299 if (buf >= 0 && att[buf].Renderbuffer) {
300 mask |= 1 << buf;
301 }
302 }
303 }
304
305 return mask;
306 }
307
308
309
310 /**
311 * New in GL 3.0
312 * Clear signed integer color buffer or stencil buffer (not depth).
313 */
314 void GLAPIENTRY
315 _mesa_ClearBufferiv(GLenum buffer, GLint drawbuffer, const GLint *value)
316 {
317 GET_CURRENT_CONTEXT(ctx);
318 FLUSH_VERTICES(ctx, 0);
319
320 FLUSH_CURRENT(ctx, 0);
321
322 if (ctx->NewState) {
323 _mesa_update_state( ctx );
324 }
325
326 switch (buffer) {
327 case GL_STENCIL:
328 /* Page 264 (page 280 of the PDF) of the OpenGL 3.0 spec says:
329 *
330 * "ClearBuffer generates an INVALID VALUE error if buffer is
331 * COLOR and drawbuffer is less than zero, or greater than the
332 * value of MAX DRAW BUFFERS minus one; or if buffer is DEPTH,
333 * STENCIL, or DEPTH STENCIL and drawbuffer is not zero."
334 */
335 if (drawbuffer != 0) {
336 _mesa_error(ctx, GL_INVALID_VALUE, "glClearBufferiv(drawbuffer=%d)",
337 drawbuffer);
338 return;
339 }
340 else if (ctx->DrawBuffer->Attachment[BUFFER_STENCIL].Renderbuffer
341 && !ctx->RasterDiscard) {
342 /* Save current stencil clear value, set to 'value', do the
343 * stencil clear and restore the clear value.
344 * XXX in the future we may have a new ctx->Driver.ClearBuffer()
345 * hook instead.
346 */
347 const GLuint clearSave = ctx->Stencil.Clear;
348 ctx->Stencil.Clear = *value;
349 ctx->Driver.Clear(ctx, BUFFER_BIT_STENCIL);
350 ctx->Stencil.Clear = clearSave;
351 }
352 break;
353 case GL_COLOR:
354 {
355 const GLbitfield mask = make_color_buffer_mask(ctx, drawbuffer);
356 if (mask == INVALID_MASK) {
357 _mesa_error(ctx, GL_INVALID_VALUE, "glClearBufferiv(drawbuffer=%d)",
358 drawbuffer);
359 return;
360 }
361 else if (mask && !ctx->RasterDiscard) {
362 union gl_color_union clearSave;
363
364 /* save color */
365 clearSave = ctx->Color.ClearColor;
366 /* set color */
367 COPY_4V(ctx->Color.ClearColor.i, value);
368 /* clear buffer(s) */
369 ctx->Driver.Clear(ctx, mask);
370 /* restore color */
371 ctx->Color.ClearColor = clearSave;
372 }
373 }
374 break;
375 case GL_DEPTH:
376 /* Page 264 (page 280 of the PDF) of the OpenGL 3.0 spec says:
377 *
378 * "The result of ClearBuffer is undefined if no conversion between
379 * the type of the specified value and the type of the buffer being
380 * cleared is defined (for example, if ClearBufferiv is called for a
381 * fixed- or floating-point buffer, or if ClearBufferfv is called
382 * for a signed or unsigned integer buffer). This is not an error."
383 *
384 * In this case we take "undefined" and "not an error" to mean "ignore."
385 * Note that we still need to generate an error for the invalid
386 * drawbuffer case (see the GL_STENCIL case above).
387 */
388 if (drawbuffer != 0) {
389 _mesa_error(ctx, GL_INVALID_VALUE, "glClearBufferiv(drawbuffer=%d)",
390 drawbuffer);
391 return;
392 }
393 return;
394 default:
395 _mesa_error(ctx, GL_INVALID_ENUM, "glClearBufferiv(buffer=%s)",
396 _mesa_lookup_enum_by_nr(buffer));
397 return;
398 }
399 }
400
401
402 /**
403 * New in GL 3.0
404 * Clear unsigned integer color buffer (not depth, not stencil).
405 */
406 void GLAPIENTRY
407 _mesa_ClearBufferuiv(GLenum buffer, GLint drawbuffer, const GLuint *value)
408 {
409 GET_CURRENT_CONTEXT(ctx);
410
411 FLUSH_VERTICES(ctx, 0);
412 FLUSH_CURRENT(ctx, 0);
413
414 if (ctx->NewState) {
415 _mesa_update_state( ctx );
416 }
417
418 switch (buffer) {
419 case GL_COLOR:
420 {
421 const GLbitfield mask = make_color_buffer_mask(ctx, drawbuffer);
422 if (mask == INVALID_MASK) {
423 _mesa_error(ctx, GL_INVALID_VALUE, "glClearBufferuiv(drawbuffer=%d)",
424 drawbuffer);
425 return;
426 }
427 else if (mask && !ctx->RasterDiscard) {
428 union gl_color_union clearSave;
429
430 /* save color */
431 clearSave = ctx->Color.ClearColor;
432 /* set color */
433 COPY_4V(ctx->Color.ClearColor.ui, value);
434 /* clear buffer(s) */
435 ctx->Driver.Clear(ctx, mask);
436 /* restore color */
437 ctx->Color.ClearColor = clearSave;
438 }
439 }
440 break;
441 case GL_DEPTH:
442 case GL_STENCIL:
443 /* Page 264 (page 280 of the PDF) of the OpenGL 3.0 spec says:
444 *
445 * "The result of ClearBuffer is undefined if no conversion between
446 * the type of the specified value and the type of the buffer being
447 * cleared is defined (for example, if ClearBufferiv is called for a
448 * fixed- or floating-point buffer, or if ClearBufferfv is called
449 * for a signed or unsigned integer buffer). This is not an error."
450 *
451 * In this case we take "undefined" and "not an error" to mean "ignore."
452 * Even though we could do something sensible for GL_STENCIL, page 263
453 * (page 279 of the PDF) says:
454 *
455 * "Only ClearBufferiv should be used to clear stencil buffers."
456 *
457 * Note that we still need to generate an error for the invalid
458 * drawbuffer case (see the GL_STENCIL case in _mesa_ClearBufferiv).
459 */
460 if (drawbuffer != 0) {
461 _mesa_error(ctx, GL_INVALID_VALUE, "glClearBufferuiv(drawbuffer=%d)",
462 drawbuffer);
463 return;
464 }
465 return;
466 default:
467 _mesa_error(ctx, GL_INVALID_ENUM, "glClearBufferuiv(buffer=%s)",
468 _mesa_lookup_enum_by_nr(buffer));
469 return;
470 }
471 }
472
473
474 /**
475 * New in GL 3.0
476 * Clear fixed-pt or float color buffer or depth buffer (not stencil).
477 */
478 void GLAPIENTRY
479 _mesa_ClearBufferfv(GLenum buffer, GLint drawbuffer, const GLfloat *value)
480 {
481 GET_CURRENT_CONTEXT(ctx);
482
483 FLUSH_VERTICES(ctx, 0);
484 FLUSH_CURRENT(ctx, 0);
485
486 if (ctx->NewState) {
487 _mesa_update_state( ctx );
488 }
489
490 switch (buffer) {
491 case GL_DEPTH:
492 /* Page 264 (page 280 of the PDF) of the OpenGL 3.0 spec says:
493 *
494 * "ClearBuffer generates an INVALID VALUE error if buffer is
495 * COLOR and drawbuffer is less than zero, or greater than the
496 * value of MAX DRAW BUFFERS minus one; or if buffer is DEPTH,
497 * STENCIL, or DEPTH STENCIL and drawbuffer is not zero."
498 */
499 if (drawbuffer != 0) {
500 _mesa_error(ctx, GL_INVALID_VALUE, "glClearBufferfv(drawbuffer=%d)",
501 drawbuffer);
502 return;
503 }
504 else if (ctx->DrawBuffer->Attachment[BUFFER_DEPTH].Renderbuffer
505 && !ctx->RasterDiscard) {
506 /* Save current depth clear value, set to 'value', do the
507 * depth clear and restore the clear value.
508 * XXX in the future we may have a new ctx->Driver.ClearBuffer()
509 * hook instead.
510 */
511 const GLclampd clearSave = ctx->Depth.Clear;
512 ctx->Depth.Clear = *value;
513 ctx->Driver.Clear(ctx, BUFFER_BIT_DEPTH);
514 ctx->Depth.Clear = clearSave;
515 }
516 /* clear depth buffer to value */
517 break;
518 case GL_COLOR:
519 {
520 const GLbitfield mask = make_color_buffer_mask(ctx, drawbuffer);
521 if (mask == INVALID_MASK) {
522 _mesa_error(ctx, GL_INVALID_VALUE, "glClearBufferfv(drawbuffer=%d)",
523 drawbuffer);
524 return;
525 }
526 else if (mask && !ctx->RasterDiscard) {
527 union gl_color_union clearSave;
528
529 /* save color */
530 clearSave = ctx->Color.ClearColor;
531 /* set color */
532 COPY_4V(ctx->Color.ClearColor.f, value);
533 /* clear buffer(s) */
534 ctx->Driver.Clear(ctx, mask);
535 /* restore color */
536 ctx->Color.ClearColor = clearSave;
537 }
538 }
539 break;
540 case GL_STENCIL:
541 /* Page 264 (page 280 of the PDF) of the OpenGL 3.0 spec says:
542 *
543 * "The result of ClearBuffer is undefined if no conversion between
544 * the type of the specified value and the type of the buffer being
545 * cleared is defined (for example, if ClearBufferiv is called for a
546 * fixed- or floating-point buffer, or if ClearBufferfv is called
547 * for a signed or unsigned integer buffer). This is not an error."
548 *
549 * In this case we take "undefined" and "not an error" to mean "ignore."
550 * Note that we still need to generate an error for the invalid
551 * drawbuffer case (see the GL_DEPTH case above).
552 */
553 if (drawbuffer != 0) {
554 _mesa_error(ctx, GL_INVALID_VALUE, "glClearBufferfv(drawbuffer=%d)",
555 drawbuffer);
556 return;
557 }
558 return;
559 default:
560 _mesa_error(ctx, GL_INVALID_ENUM, "glClearBufferfv(buffer=%s)",
561 _mesa_lookup_enum_by_nr(buffer));
562 return;
563 }
564 }
565
566
567 /**
568 * New in GL 3.0
569 * Clear depth/stencil buffer only.
570 */
571 void GLAPIENTRY
572 _mesa_ClearBufferfi(GLenum buffer, GLint drawbuffer,
573 GLfloat depth, GLint stencil)
574 {
575 GET_CURRENT_CONTEXT(ctx);
576 GLbitfield mask = 0;
577
578 FLUSH_VERTICES(ctx, 0);
579 FLUSH_CURRENT(ctx, 0);
580
581 if (buffer != GL_DEPTH_STENCIL) {
582 _mesa_error(ctx, GL_INVALID_ENUM, "glClearBufferfi(buffer=%s)",
583 _mesa_lookup_enum_by_nr(buffer));
584 return;
585 }
586
587 /* Page 264 (page 280 of the PDF) of the OpenGL 3.0 spec says:
588 *
589 * "ClearBuffer generates an INVALID VALUE error if buffer is
590 * COLOR and drawbuffer is less than zero, or greater than the
591 * value of MAX DRAW BUFFERS minus one; or if buffer is DEPTH,
592 * STENCIL, or DEPTH STENCIL and drawbuffer is not zero."
593 */
594 if (drawbuffer != 0) {
595 _mesa_error(ctx, GL_INVALID_VALUE, "glClearBufferfi(drawbuffer=%d)",
596 drawbuffer);
597 return;
598 }
599
600 if (ctx->RasterDiscard)
601 return;
602
603 if (ctx->NewState) {
604 _mesa_update_state( ctx );
605 }
606
607 if (ctx->DrawBuffer->Attachment[BUFFER_DEPTH].Renderbuffer)
608 mask |= BUFFER_BIT_DEPTH;
609 if (ctx->DrawBuffer->Attachment[BUFFER_STENCIL].Renderbuffer)
610 mask |= BUFFER_BIT_STENCIL;
611
612 if (mask) {
613 /* save current clear values */
614 const GLclampd clearDepthSave = ctx->Depth.Clear;
615 const GLuint clearStencilSave = ctx->Stencil.Clear;
616
617 /* set new clear values */
618 ctx->Depth.Clear = depth;
619 ctx->Stencil.Clear = stencil;
620
621 /* clear buffers */
622 ctx->Driver.Clear(ctx, mask);
623
624 /* restore */
625 ctx->Depth.Clear = clearDepthSave;
626 ctx->Stencil.Clear = clearStencilSave;
627 }
628 }