1 /*
2    (c) Copyright 2001-2009  The world wide DirectFB Open Source Community (directfb.org)
3    (c) Copyright 2000-2004  Convergence (integrated media) GmbH
4 
5    All rights reserved.
6 
7    Written by Denis Oliver Kropp <dok@directfb.org>,
8               Andreas Hundt <andi@fischlustig.de>,
9               Sven Neumann <neo@directfb.org>,
10               Ville Syrjälä <syrjala@sci.fi> and
11               Claudio Ciccani <klan@users.sf.net>.
12 
13    This library is free software; you can redistribute it and/or
14    modify it under the terms of the GNU Lesser General Public
15    License as published by the Free Software Foundation; either
16    version 2 of the License, or (at your option) any later version.
17 
18    This library is distributed in the hope that it will be useful,
19    but WITHOUT ANY WARRANTY; without even the implied warranty of
20    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
21    Lesser General Public License for more details.
22 
23    You should have received a copy of the GNU Lesser General Public
24    License along with this library; if not, write to the
25    Free Software Foundation, Inc., 59 Temple Place - Suite 330,
26    Boston, MA 02111-1307, USA.
27 */
28 
29 #include <config.h>
30 
31 #include <direct/debug.h>
32 #include <direct/mem.h>
33 #include <direct/memcpy.h>
34 #include <direct/messages.h>
35 
36 #include <core/core.h>
37 #include <core/graphics_state.h>
38 #include <core/state.h>
39 #include <core/surface.h>
40 
41 #include <core/CoreDFB.h>
42 #include <core/CoreGraphicsState.h>
43 #include <core/CoreGraphicsStateClient.h>
44 
45 #include <fusion/conf.h>
46 
47 D_DEBUG_DOMAIN( Core_GraphicsStateClient, "Core/GfxState/Client", "DirectFB Core Graphics State Client" );
48 
49 /**********************************************************************************************************************/
50 
51 DFBResult
CoreGraphicsStateClient_Init(CoreGraphicsStateClient * client,CardState * state)52 CoreGraphicsStateClient_Init( CoreGraphicsStateClient *client,
53                               CardState               *state )
54 {
55      DFBResult ret;
56 
57      D_DEBUG_AT( Core_GraphicsStateClient, "%s( client %p, state %p )\n", __FUNCTION__, client, state );
58 
59      D_ASSERT( client != NULL );
60      D_MAGIC_ASSERT( state, CardState );
61      D_MAGIC_ASSERT( state->core, CoreDFB );
62 
63      client->core  = state->core;
64      client->state = state;
65 
66      ret = CoreDFB_CreateState( state->core, &client->gfx_state );
67      if (ret)
68           return ret;
69 
70      D_MAGIC_SET( client, CoreGraphicsStateClient );
71 
72      return DFB_OK;
73 }
74 
75 void
CoreGraphicsStateClient_Deinit(CoreGraphicsStateClient * client)76 CoreGraphicsStateClient_Deinit( CoreGraphicsStateClient *client )
77 {
78      D_DEBUG_AT( Core_GraphicsStateClient, "%s( client %p )\n", __FUNCTION__, client );
79 
80      D_MAGIC_ASSERT( client, CoreGraphicsStateClient );
81 
82      dfb_graphics_state_unref( client->gfx_state );
83 
84      D_MAGIC_CLEAR( client );
85 }
86 
87 DFBResult
CoreGraphicsStateClient_SetState(CoreGraphicsStateClient * client,CardState * state,StateModificationFlags flags)88 CoreGraphicsStateClient_SetState( CoreGraphicsStateClient *client,
89                                   CardState               *state,
90                                   StateModificationFlags   flags )
91 {
92      DFBResult ret;
93 
94      D_DEBUG_AT( Core_GraphicsStateClient, "%s( client %p, state %p, flags 0x%08x )\n", __FUNCTION__, client, state, flags );
95 
96      D_MAGIC_ASSERT( client, CoreGraphicsStateClient );
97      D_MAGIC_ASSERT( state, CardState );
98 
99      if (flags & SMF_DRAWING_FLAGS) {
100           ret = CoreGraphicsState_SetDrawingFlags( client->gfx_state, state->drawingflags );
101           if (ret)
102                return ret;
103      }
104 
105      if (flags & SMF_BLITTING_FLAGS) {
106           ret = CoreGraphicsState_SetBlittingFlags( client->gfx_state, state->blittingflags );
107           if (ret)
108                return ret;
109      }
110 
111      if (flags & SMF_CLIP) {
112           ret = CoreGraphicsState_SetClip( client->gfx_state, &state->clip );
113           if (ret)
114                return ret;
115      }
116 
117      if (flags & SMF_COLOR) {
118           ret = CoreGraphicsState_SetColor( client->gfx_state, &state->color );
119           if (ret)
120                return ret;
121      }
122 
123      if (flags & SMF_SRC_BLEND) {
124           ret = CoreGraphicsState_SetSrcBlend( client->gfx_state, state->src_blend );
125           if (ret)
126                return ret;
127      }
128 
129      if (flags & SMF_DST_BLEND) {
130           ret = CoreGraphicsState_SetDstBlend( client->gfx_state, state->dst_blend );
131           if (ret)
132                return ret;
133      }
134 
135      if (flags & SMF_SRC_COLORKEY) {
136           ret = CoreGraphicsState_SetSrcColorKey( client->gfx_state, state->src_colorkey );
137           if (ret)
138                return ret;
139      }
140 
141      if (flags & SMF_DST_COLORKEY) {
142           ret = CoreGraphicsState_SetDstColorKey( client->gfx_state, state->dst_colorkey );
143           if (ret)
144                return ret;
145      }
146 
147      if (flags & SMF_DESTINATION) {
148           D_DEBUG_AT( Core_GraphicsStateClient, "  -> DESTINATION %p [%d]\n", state->destination, state->destination->object.id );
149 
150           ret = CoreGraphicsState_SetDestination( client->gfx_state, state->destination );
151           if (ret)
152                return ret;
153      }
154 
155      if (flags & SMF_SOURCE) {
156           ret = CoreGraphicsState_SetSource( client->gfx_state, state->source );
157           if (ret)
158                return ret;
159      }
160 
161      if (flags & SMF_SOURCE_MASK) {
162           ret = CoreGraphicsState_SetSourceMask( client->gfx_state, state->source_mask );
163           if (ret)
164                return ret;
165      }
166 
167      if (flags & SMF_SOURCE_MASK_VALS) {
168           ret = CoreGraphicsState_SetSourceMaskVals( client->gfx_state, &state->src_mask_offset, state->src_mask_flags );
169           if (ret)
170                return ret;
171      }
172 
173      if (flags & SMF_INDEX_TRANSLATION) {
174           ret = CoreGraphicsState_SetIndexTranslation( client->gfx_state, state->index_translation, state->num_translation );
175           if (ret)
176                return ret;
177      }
178 
179      if (flags & SMF_COLORKEY) {
180           ret = CoreGraphicsState_SetColorKey( client->gfx_state, &state->colorkey );
181           if (ret)
182                return ret;
183      }
184 
185      if (flags & SMF_RENDER_OPTIONS) {
186           ret = CoreGraphicsState_SetRenderOptions( client->gfx_state, state->render_options );
187           if (ret)
188                return ret;
189      }
190 
191      if (flags & SMF_MATRIX) {
192           ret = CoreGraphicsState_SetMatrix( client->gfx_state, state->matrix );
193           if (ret)
194                return ret;
195      }
196 
197      if (flags & SMF_SOURCE2) {
198           ret = CoreGraphicsState_SetSource2( client->gfx_state, state->source2 );
199           if (ret)
200                return ret;
201      }
202 
203      return DFB_OK;
204 }
205 
206 DFBResult
CoreGraphicsStateClient_Update(CoreGraphicsStateClient * client,DFBAccelerationMask accel,CardState * state)207 CoreGraphicsStateClient_Update( CoreGraphicsStateClient *client,
208                                 DFBAccelerationMask      accel,
209                                 CardState               *state )
210 {
211      DFBResult              ret;
212      StateModificationFlags flags = SMF_DESTINATION | SMF_CLIP | SMF_RENDER_OPTIONS;
213 
214      D_DEBUG_AT( Core_GraphicsStateClient, "%s( client %p )\n", __FUNCTION__, client );
215 
216      D_MAGIC_ASSERT( client, CoreGraphicsStateClient );
217      D_MAGIC_ASSERT( state, CardState );
218 
219      /*
220       * dfb_gfxcard_state_check() moves flags to mod_hw,
221       * called from IDirectFBSurface::GetAccelerationMask().
222       *
223       * FIXME: Add GetAccelerationMask() to CoreGraphicsState flux
224       *        and do not load the graphics driver at slaves anymore.
225       */
226      state->modified |= state->mod_hw;
227      state->mod_hw    = 0;
228 
229      if (state->render_options & DSRO_MATRIX)
230           flags |= SMF_MATRIX;
231 
232      if (DFB_DRAWING_FUNCTION( accel )) {
233           flags |= SMF_DRAWING_FLAGS | SMF_COLOR;
234 
235           if (state->drawingflags & DSDRAW_BLEND)
236                flags |= SMF_SRC_BLEND | SMF_DST_BLEND;
237 
238           if (state->drawingflags & DSDRAW_DST_COLORKEY)
239                flags |= SMF_DST_COLORKEY;
240      }
241      else {
242           flags |= SMF_BLITTING_FLAGS | SMF_SOURCE;
243 
244           if (accel == DFXL_BLIT2)
245                flags |= SMF_SOURCE2;
246 
247           if (state->blittingflags & (DSBLIT_BLEND_COLORALPHA |
248                                       DSBLIT_COLORIZE |
249                                       DSBLIT_SRC_PREMULTCOLOR))
250                flags |= SMF_COLOR;
251 
252           if (state->blittingflags & (DSBLIT_BLEND_ALPHACHANNEL |
253                                       DSBLIT_BLEND_COLORALPHA))
254                flags |= SMF_SRC_BLEND | SMF_DST_BLEND;
255 
256           if (state->blittingflags & DSBLIT_SRC_COLORKEY)
257                flags |= SMF_SRC_COLORKEY;
258 
259           if (state->blittingflags & DSBLIT_DST_COLORKEY)
260                flags |= SMF_DST_COLORKEY;
261 
262           if (state->blittingflags & (DSBLIT_SRC_MASK_ALPHA | DSBLIT_SRC_MASK_COLOR))
263                flags |= SMF_SOURCE_MASK | SMF_SOURCE_MASK_VALS;
264 
265           if (state->blittingflags & DSBLIT_INDEX_TRANSLATION)
266                flags |= SMF_INDEX_TRANSLATION;
267 
268           if (state->blittingflags & DSBLIT_COLORKEY_PROTECT)
269                flags |= SMF_COLORKEY;
270      }
271 
272      ret = CoreGraphicsStateClient_SetState( client, state, state->modified & flags );
273      if (ret)
274           return ret;
275 
276      state->modified &= ~flags;
277 
278      return DFB_OK;
279 }
280 
281 DFBResult
CoreGraphicsStateClient_DrawRectangles(CoreGraphicsStateClient * client,const DFBRectangle * rects,unsigned int num)282 CoreGraphicsStateClient_DrawRectangles( CoreGraphicsStateClient *client,
283                                         const DFBRectangle      *rects,
284                                         unsigned int             num )
285 {
286      D_DEBUG_AT( Core_GraphicsStateClient, "%s( client %p )\n", __FUNCTION__, client );
287 
288      D_MAGIC_ASSERT( client, CoreGraphicsStateClient );
289      D_ASSERT( rects != NULL );
290 
291      if (dfb_core_is_master( client->core ) || !fusion_config->secure_fusion) {
292           unsigned int i;
293 
294           for (i=0; i<num; i++)
295                // FIXME: will overwrite rects
296                dfb_gfxcard_drawrectangle( (DFBRectangle*) &rects[i], client->state );
297      }
298      else {
299           DFBResult ret;
300 
301           CoreGraphicsStateClient_Update( client, DFXL_DRAWRECTANGLE, client->state );
302 
303           ret = CoreGraphicsState_DrawRectangles( client->gfx_state, rects, num );
304           if (ret)
305                return ret;
306      }
307 
308      return DFB_OK;
309 }
310 
311 DFBResult
CoreGraphicsStateClient_DrawLines(CoreGraphicsStateClient * client,const DFBRegion * lines,unsigned int num)312 CoreGraphicsStateClient_DrawLines( CoreGraphicsStateClient *client,
313                                    const DFBRegion         *lines,
314                                    unsigned int             num )
315 {
316      D_DEBUG_AT( Core_GraphicsStateClient, "%s( client %p )\n", __FUNCTION__, client );
317 
318      D_MAGIC_ASSERT( client, CoreGraphicsStateClient );
319      D_ASSERT( lines != NULL );
320 
321      if (dfb_core_is_master( client->core ) || !fusion_config->secure_fusion) {
322           // FIXME: will overwrite lines
323           dfb_gfxcard_drawlines( (DFBRegion*) lines, num, client->state );
324      }
325      else {
326           DFBResult ret;
327 
328           CoreGraphicsStateClient_Update( client, DFXL_DRAWLINE, client->state );
329 
330           ret = CoreGraphicsState_DrawLines( client->gfx_state, lines, num );
331           if (ret)
332                return ret;
333      }
334 
335      return DFB_OK;
336 }
337 
338 DFBResult
CoreGraphicsStateClient_FillRectangles(CoreGraphicsStateClient * client,const DFBRectangle * rects,unsigned int num)339 CoreGraphicsStateClient_FillRectangles( CoreGraphicsStateClient *client,
340                                         const DFBRectangle      *rects,
341                                         unsigned int             num )
342 {
343      D_DEBUG_AT( Core_GraphicsStateClient, "%s( client %p )\n", __FUNCTION__, client );
344 
345      D_MAGIC_ASSERT( client, CoreGraphicsStateClient );
346      D_ASSERT( rects != NULL );
347 
348      if (dfb_core_is_master( client->core ) || !fusion_config->secure_fusion) {
349           dfb_gfxcard_fillrectangles( rects, num, client->state );
350      }
351      else {
352           DFBResult ret;
353 
354           CoreGraphicsStateClient_Update( client, DFXL_FILLRECTANGLE, client->state );
355 
356           ret = CoreGraphicsState_FillRectangles( client->gfx_state, rects, num );
357           if (ret)
358                return ret;
359      }
360 
361      return DFB_OK;
362 }
363 
364 DFBResult
CoreGraphicsStateClient_FillTriangles(CoreGraphicsStateClient * client,const DFBTriangle * triangles,unsigned int num)365 CoreGraphicsStateClient_FillTriangles( CoreGraphicsStateClient *client,
366                                        const DFBTriangle       *triangles,
367                                        unsigned int             num )
368 {
369      D_DEBUG_AT( Core_GraphicsStateClient, "%s( client %p )\n", __FUNCTION__, client );
370 
371      D_MAGIC_ASSERT( client, CoreGraphicsStateClient );
372      D_ASSERT( triangles != NULL );
373 
374      if (dfb_core_is_master( client->core ) || !fusion_config->secure_fusion) {
375           dfb_gfxcard_filltriangles( triangles, num, client->state );
376      }
377      else {
378           DFBResult ret;
379 
380           CoreGraphicsStateClient_Update( client, DFXL_FILLTRIANGLE, client->state );
381 
382           ret = CoreGraphicsState_FillTriangles( client->gfx_state, triangles, num );
383           if (ret)
384                return ret;
385      }
386 
387      return DFB_OK;
388 }
389 
390 DFBResult
CoreGraphicsStateClient_FillTrapezoids(CoreGraphicsStateClient * client,const DFBTrapezoid * trapezoids,unsigned int num)391 CoreGraphicsStateClient_FillTrapezoids( CoreGraphicsStateClient *client,
392                                         const DFBTrapezoid      *trapezoids,
393                                         unsigned int             num )
394 {
395      D_DEBUG_AT( Core_GraphicsStateClient, "%s( client %p )\n", __FUNCTION__, client );
396 
397      D_MAGIC_ASSERT( client, CoreGraphicsStateClient );
398      D_ASSERT( trapezoids != NULL );
399 
400      if (dfb_core_is_master( client->core ) || !fusion_config->secure_fusion) {
401           dfb_gfxcard_filltrapezoids( trapezoids, num, client->state );
402      }
403      else {
404           DFBResult ret;
405 
406           CoreGraphicsStateClient_Update( client, DFXL_FILLTRAPEZOID, client->state );
407 
408           ret = CoreGraphicsState_FillTrapezoids( client->gfx_state, trapezoids, num );
409           if (ret)
410                return ret;
411      }
412 
413      return DFB_OK;
414 }
415 
416 DFBResult
CoreGraphicsStateClient_FillSpans(CoreGraphicsStateClient * client,int y,const DFBSpan * spans,unsigned int num)417 CoreGraphicsStateClient_FillSpans( CoreGraphicsStateClient *client,
418                                    int                      y,
419                                    const DFBSpan           *spans,
420                                    unsigned int             num )
421 {
422      D_DEBUG_AT( Core_GraphicsStateClient, "%s( client %p )\n", __FUNCTION__, client );
423 
424      D_MAGIC_ASSERT( client, CoreGraphicsStateClient );
425      D_ASSERT( spans != NULL );
426 
427      if (dfb_core_is_master( client->core ) || !fusion_config->secure_fusion) {
428           // FIXME: may overwrite spans
429           dfb_gfxcard_fillspans( y, (DFBSpan*) spans, num, client->state );
430      }
431      else {
432           DFBResult ret;
433 
434           CoreGraphicsStateClient_Update( client, DFXL_FILLRECTANGLE, client->state );
435 
436           ret = CoreGraphicsState_FillSpans( client->gfx_state, y, spans, num );
437           if (ret)
438                return ret;
439      }
440 
441      return DFB_OK;
442 }
443 
444 DFBResult
CoreGraphicsStateClient_Blit(CoreGraphicsStateClient * client,const DFBRectangle * rects,const DFBPoint * points,unsigned int num)445 CoreGraphicsStateClient_Blit( CoreGraphicsStateClient *client,
446                               const DFBRectangle      *rects,
447                               const DFBPoint          *points,
448                               unsigned int             num )
449 {
450      D_DEBUG_AT( Core_GraphicsStateClient, "%s( client %p )\n", __FUNCTION__, client );
451 
452      D_MAGIC_ASSERT( client, CoreGraphicsStateClient );
453      D_ASSERT( rects != NULL );
454      D_ASSERT( points != NULL );
455 
456      if (dfb_core_is_master( client->core ) || !fusion_config->secure_fusion) {
457           // FIXME: will overwrite rects, points
458           if (num > 1)
459                dfb_gfxcard_batchblit( (DFBRectangle*) rects, (DFBPoint*) points, num, client->state );
460           else
461                dfb_gfxcard_blit( (DFBRectangle*) rects, ((DFBPoint*)points)->x, ((DFBPoint*)points)->y, client->state );
462      }
463      else {
464           DFBResult ret;
465 
466           CoreGraphicsStateClient_Update( client, DFXL_BLIT, client->state );
467 
468           ret = CoreGraphicsState_Blit( client->gfx_state, rects, points, num );
469           if (ret)
470                return ret;
471      }
472 
473      return DFB_OK;
474 }
475 
476 DFBResult
CoreGraphicsStateClient_Blit2(CoreGraphicsStateClient * client,const DFBRectangle * rects,const DFBPoint * points1,const DFBPoint * points2,unsigned int num)477 CoreGraphicsStateClient_Blit2( CoreGraphicsStateClient *client,
478                                const DFBRectangle      *rects,
479                                const DFBPoint          *points1,
480                                const DFBPoint          *points2,
481                                unsigned int             num )
482 {
483      D_DEBUG_AT( Core_GraphicsStateClient, "%s( client %p )\n", __FUNCTION__, client );
484 
485      D_MAGIC_ASSERT( client, CoreGraphicsStateClient );
486      D_ASSERT( rects != NULL );
487      D_ASSERT( points1 != NULL );
488      D_ASSERT( points2 != NULL );
489 
490      if (dfb_core_is_master( client->core ) || !fusion_config->secure_fusion) {
491           // FIXME: will overwrite rects, points
492           dfb_gfxcard_batchblit2( (DFBRectangle*) rects, (DFBPoint*) points1, (DFBPoint*) points2, num, client->state );
493      }
494      else {
495           DFBResult ret;
496 
497           CoreGraphicsStateClient_Update( client, DFXL_BLIT2, client->state );
498 
499           ret = CoreGraphicsState_Blit2( client->gfx_state, rects, points1, points2, num );
500           if (ret)
501                return ret;
502      }
503 
504      return DFB_OK;
505 }
506 
507 DFBResult
CoreGraphicsStateClient_StretchBlit(CoreGraphicsStateClient * client,const DFBRectangle * srects,const DFBRectangle * drects,unsigned int num)508 CoreGraphicsStateClient_StretchBlit( CoreGraphicsStateClient *client,
509                                      const DFBRectangle      *srects,
510                                      const DFBRectangle      *drects,
511                                      unsigned int             num )
512 {
513      D_DEBUG_AT( Core_GraphicsStateClient, "%s( client %p )\n", __FUNCTION__, client );
514 
515      D_MAGIC_ASSERT( client, CoreGraphicsStateClient );
516      D_ASSERT( srects != NULL );
517      D_ASSERT( drects != NULL );
518 
519      if (num == 0)
520           return DFB_OK;
521 
522      if (num != 1)
523           D_UNIMPLEMENTED();
524 
525      if (dfb_core_is_master( client->core ) || !fusion_config->secure_fusion) {
526           // FIXME: will overwrite rects
527           dfb_gfxcard_stretchblit( (DFBRectangle*) srects, (DFBRectangle*) drects, client->state );
528      }
529      else {
530           DFBResult ret;
531 
532           CoreGraphicsStateClient_Update( client, DFXL_STRETCHBLIT, client->state );
533 
534           ret = CoreGraphicsState_StretchBlit( client->gfx_state, srects, drects, num );
535           if (ret)
536                return ret;
537      }
538 
539      return DFB_OK;
540 }
541 
542 DFBResult
CoreGraphicsStateClient_TileBlit(CoreGraphicsStateClient * client,const DFBRectangle * rects,const DFBPoint * points1,const DFBPoint * points2,unsigned int num)543 CoreGraphicsStateClient_TileBlit( CoreGraphicsStateClient *client,
544                                   const DFBRectangle      *rects,
545                                   const DFBPoint          *points1,
546                                   const DFBPoint          *points2,
547                                   unsigned int             num )
548 {
549      D_DEBUG_AT( Core_GraphicsStateClient, "%s( client %p )\n", __FUNCTION__, client );
550 
551      D_MAGIC_ASSERT( client, CoreGraphicsStateClient );
552      D_ASSERT( rects != NULL );
553      D_ASSERT( points1 != NULL );
554      D_ASSERT( points2 != NULL );
555 
556      if (dfb_core_is_master( client->core ) || !fusion_config->secure_fusion) {
557           u32 i;
558 
559           // FIXME: will overwrite rects, points
560           for (i=0; i<num; i++)
561                dfb_gfxcard_tileblit( (DFBRectangle*) &rects[i], points1[i].x, points1[i].y, points2[i].x, points2[i].y, client->state );
562      }
563      else {
564           DFBResult ret;
565 
566           CoreGraphicsStateClient_Update( client, DFXL_BLIT, client->state );
567 
568           ret = CoreGraphicsState_TileBlit( client->gfx_state, rects, points1, points2, num );
569           if (ret)
570                return ret;
571      }
572 
573      return DFB_OK;
574 }
575 
576 DFBResult
CoreGraphicsStateClient_TextureTriangles(CoreGraphicsStateClient * client,const DFBVertex * vertices,int num,DFBTriangleFormation formation)577 CoreGraphicsStateClient_TextureTriangles( CoreGraphicsStateClient *client,
578                                           const DFBVertex         *vertices,
579                                           int                      num,
580                                           DFBTriangleFormation     formation )
581 {
582      D_DEBUG_AT( Core_GraphicsStateClient, "%s( client %p )\n", __FUNCTION__, client );
583 
584      D_MAGIC_ASSERT( client, CoreGraphicsStateClient );
585      D_ASSERT( vertices != NULL );
586 
587      if (dfb_core_is_master( client->core ) || !fusion_config->secure_fusion) {
588           // FIXME: may overwrite vertices
589           dfb_gfxcard_texture_triangles( (DFBVertex*) vertices, num, formation, client->state );
590      }
591      else {
592           DFBResult ret;
593 
594           CoreGraphicsStateClient_Update( client, DFXL_TEXTRIANGLES, client->state );
595 
596           ret = CoreGraphicsState_TextureTriangles( client->gfx_state, vertices, num, formation );
597           if (ret)
598                return ret;
599      }
600 
601      return DFB_OK;
602 }
603 
604