2 * This file is part of NumptyPhysics
3 * Copyright (C) 2008 Tim Edmonds
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License as
7 * published by the Free Software Foundation; either version 3 of the
8 * License, or (at your option) any later version.
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * General Public License for more details.
24 #include <SDL/SDL_image.h>
26 #define Window X11Window //oops
27 #define Font X11Font //oops
28 #include <SDL/SDL_syswm.h>
36 extern SDL_Surface
*zoomSurface(SDL_Surface
* src
, double zoomx
, double zoomy
);
39 // extract RGB colour components as 8bit values from RGB888
40 #define R32(p) (((p)>>16)&0xff)
41 #define G32(p) (((p)>>8)&0xff)
42 #define B32(p) ((p)&0xff)
44 // extract RGB colour components as 8bit values from RGB565
45 #define R16(p) (((p)>>8)&0xf8)
46 #define G16(p) (((p)>>3)&0xfc)
47 #define B16(p) (((p)<<3)&0xf8)
49 #define R16G16B16_TO_RGB888(r,g,b) \
50 ((((r)<<8)&0xff0000) | ((g)&0x00ff00) | (((b)>>8)))
52 #define R16G16B16_TO_RGB565(r,g,b) \
53 ((Uint16)( (((r)&0xf800) | (((g)>>5)&0x07e0) | (((b)>>11))&0x001f) ))
55 #define RGB888_TO_RGB565(p) \
56 ((Uint16)( (((p)>>8)&0xf800) | (((p)>>5)&0x07e0) | (((p)>>3)&0x001f) ))
59 void ExtractRgb( uint32 c
, int& r
, int &g
, int &b
)
61 r
= R32(c
); g
= G32(c
); b
= B32(c
);
64 void ExtractRgb( uint16 c
, int& r
, int &g
, int &b
)
66 r
= R16(c
); g
= G16(c
); b
= B16(c
);
70 template <typename PIX
>
71 inline void AlphaBlend( PIX
& p
, int cr
, int cg
, int cb
, int a
, int ia
)
73 throw "not implemented";
76 inline void AlphaBlend( Uint16
& p
, int cr
, int cg
, int cb
, int a
, int ia
)
78 p
= R16G16B16_TO_RGB565( a
* cr
+ ia
* R16(p
),
80 a
* cb
+ ia
* B16(p
) );
83 inline void AlphaBlend( Uint32
& p
, int cr
, int cg
, int cb
, int a
, int ia
)
86 p
= R16G16B16_TO_RGB888( a
* cr
+ ia
* R32(p
),
88 a
* cb
+ ia
* B32(p
) );
91 #define ALPHA_MAX 0xff
93 template <typename PIX
, unsigned W
>
96 int m_r
, m_g
, m_b
, m_c
;
97 inline AlphaBrush( PIX c
)
100 ExtractRgb( c
, m_r
, m_g
, m_b
);
102 inline void ink( PIX
* pix
, int step
, int a
)
104 int ia
= ALPHA_MAX
- a
;
106 AlphaBlend( *(pix
+o
*step
), m_r
, m_g
, m_b
, a
, ia
);
107 for ( ; o
<=W
/2; o
++ ) {
110 AlphaBlend( *(pix
+o
*step
), m_r
, m_g
, m_b
, ia
, a
);
114 template <typename PIX
>
115 struct AlphaBrush
<PIX
,1>
117 int m_r
, m_g
, m_b
, m_c
;
118 inline AlphaBrush( PIX c
)
121 ExtractRgb( c
, m_r
, m_g
, m_b
);
123 inline void ink( PIX
* pix
, int step
, int a
)
125 int ia
= ALPHA_MAX
- a
;
126 AlphaBlend( *(pix
-step
), m_r
, m_g
, m_b
, a
, ia
);
127 AlphaBlend( *(pix
), m_r
, m_g
, m_b
, ia
, a
);
131 template <typename PIX
>
132 struct AlphaBrush
<PIX
,3>
134 int m_r
, m_g
, m_b
, m_c
;
135 inline AlphaBrush( PIX c
)
138 ExtractRgb( c
, m_r
, m_g
, m_b
);
140 inline void ink( PIX
* pix
, int step
, int a
)
142 int ia
= ALPHA_MAX
- a
;
143 AlphaBlend( *(pix
-step
), m_r
, m_g
, m_b
, a
, ia
);
145 AlphaBlend( *(pix
+step
), m_r
, m_g
, m_b
, ia
, a
);
151 template <typename PIX
, unsigned THICK
>
152 inline void renderLine( void *buf
,
154 int x1
, int y1
, int x2
, int y2
,
157 PIX
*pix
= (PIX
*)((char*)buf
+byteStride
*y1
) + x1
;
158 int lg_delta
, sh_delta
, cycle
, lg_step
, sh_step
;
159 int alpha
, alpha_step
, alpha_reset
;
160 int pixStride
= byteStride
/sizeof(PIX
);
161 AlphaBrush
<PIX
,THICK
> brush( color
);
165 lg_step
= Sgn(lg_delta
);
166 lg_delta
= Abs(lg_delta
);
167 sh_step
= Sgn(sh_delta
);
168 sh_delta
= Abs(sh_delta
);
169 if ( sh_step
< 0 ) pixStride
= -pixStride
;
171 // in theory should be able to do this with just a single step
172 // variable - ie: combine cycle and alpha as in wu algorithm
173 if (sh_delta
< lg_delta
) {
174 cycle
= lg_delta
>> 1;
175 alpha
= ALPHA_MAX
>> 1;
176 alpha_step
= -(ALPHA_MAX
* sh_delta
/(lg_delta
+1));
177 alpha_reset
= alpha_step
< 0 ? ALPHA_MAX
: 0;
178 int count
= lg_step
>0 ? x2
-x1
: x1
-x2
;
180 brush
.ink( pix
, pixStride
, alpha
);
184 if (cycle
> lg_delta
) {
191 cycle
= sh_delta
>> 1;
192 alpha
= ALPHA_MAX
>> 1;
193 alpha_step
= -lg_step
* Abs(ALPHA_MAX
* lg_delta
/(sh_delta
+1));
194 alpha_reset
= alpha_step
< 0 ? ALPHA_MAX
: 0;
195 int count
= sh_step
>0 ? y2
-y1
: y1
-y2
;
197 brush
.ink( pix
, 1, alpha
);
201 if (cycle
> sh_delta
) {
211 #define SURFACE(cANVASpTR) ((SDL_Surface*)((cANVASpTR)->m_state))
213 Canvas::Canvas( int w
, int h
)
218 switch (SDL_GetVideoInfo()->vfmt
->BitsPerPixel
) {
221 m_state
= SDL_CreateRGBSurface( SDL_SWSURFACE
, w
, h
,
222 SDL_GetVideoInfo()->vfmt
->BitsPerPixel
,
223 SDL_GetVideoInfo()->vfmt
->Rmask
,
224 SDL_GetVideoInfo()->vfmt
->Gmask
,
225 SDL_GetVideoInfo()->vfmt
->Bmask
,
226 SDL_GetVideoInfo()->vfmt
->Amask
);
229 // eg: dummy vid driver reports 8bpp
230 m_state
= SDL_CreateRGBSurface( SDL_SWSURFACE
, w
, h
, 32,
231 0xFF0000, 0x00FF00, 0x0000FF, 0xFF000000 );
238 Canvas::Canvas( State state
)
250 int Canvas::width() const
252 return SURFACE(this)->w
;
255 int Canvas::height() const
257 return SURFACE(this)->h
;
260 int Canvas::makeColour( int r
, int g
, int b
) const
262 return SDL_MapRGB( SURFACE(this)->format
, r
, g
, b
);
265 int Canvas::makeColour( int c
) const
267 return SDL_MapRGB( SURFACE(this)->format
,
268 (c
>>16)&0xff, (c
>>8)&0xff, (c
>>0)&0xff );
271 void Canvas::resetClip()
274 setClip( 0, 0, width(), height() );
276 setClip( 0, 0, 0, 0 );
280 void Canvas::setClip( int x
, int y
, int w
, int h
)
282 m_clip
= Rect(x
,y
,x
+w
-1,y
+h
-1);
285 void Canvas::setBackground( int c
)
290 void Canvas::setBackground( Canvas
* bg
)
298 SDL_BlitSurface( SURFACE(m_bgImage
), NULL
, SURFACE(this), NULL
);
300 SDL_FillRect( SURFACE(this), NULL
, m_bgColour
);
304 void Canvas::fade( const Rect
& rr
)
309 bpp
= SURFACE(this)->format
->BytesPerPixel
;
310 char* row
= (char*)SURFACE(this)->pixels
;
311 int pixStride
= width();
312 int w
= r
.br
.x
- r
.tl
.x
+ 1;
313 int h
= r
.br
.y
- r
.tl
.y
+ 1;
314 row
+= (r
.tl
.x
+ r
.tl
.y
* pixStride
) * bpp
;
316 SDL_LockSurface(SURFACE(this));
319 for ( int r
=h
; r
>0; r
-- ) {
320 for ( int i
=0;i
<w
;i
++) {
321 ((Uint16
*)row
)[i
] = (((Uint16
*)row
)[i
]>>1) & 0x7bef;
323 row
+= pixStride
* bpp
;
327 for ( int r
=h
; r
>0; r
-- ) {
328 for ( int i
=0;i
<w
;i
++) {
329 ((Uint32
*)row
)[i
] = (((Uint32
*)row
)[i
]>>1) & 0x7f7f7f;
331 row
+= pixStride
* bpp
;
335 SDL_UnlockSurface(SURFACE(this));
338 Canvas
* Canvas::scale( int factor
) const
340 Canvas
*c
= new Canvas( width()/factor
, height()/factor
);
342 if ( factor
==4 && SURFACE(this)->format
->BytesPerPixel
==2 ) {
343 const uint16 MASK2LSB
= 0xe79c;
344 int dpitch
= SURFACE(c
)->pitch
/ sizeof(uint16_t);
345 int spitch
= SURFACE(this)->pitch
/ sizeof(uint16_t);
346 uint16_t *drow
= (uint16_t*)SURFACE(c
)->pixels
;
347 for ( int y
=0;y
<c
->height();y
++ ) {
348 for ( int x
=0;x
<c
->width();x
++ ) {
350 uint16_t *srow
= (uint16_t*)SURFACE(this)->pixels
351 + (y
*spitch
+x
)*factor
;
352 for ( int yy
=0;yy
<factor
;yy
++ ) {
354 for ( int xx
=0;xx
<factor
;xx
++ ) {
355 q
+= (srow
[xx
]&MASK2LSB
)>>2;
357 p
+= (q
&MASK2LSB
)>>2;
365 for ( int y
=0;y
<c
->height();y
++ ) {
366 for ( int x
=0;x
<c
->width();x
++ ) {
369 for ( int yy
=0;yy
<factor
;yy
++ ) {
370 for ( int xx
=0;xx
<factor
;xx
++ ) {
371 SDL_GetRGB( readPixel( x
*factor
+xx
, y
*factor
+yy
),
372 SURFACE(this)->format
, &rr
,&gg
,&bb
);
378 int div
= factor
*factor
;
379 c
->drawPixel( x
, y
, makeColour(r
/div
,g
/div
,b
/div
) );
388 void Canvas::scale( int w
, int h
)
390 if ( w
!=width() && h
!=height() ) {
391 SDL_Surface
*s
= zoomSurface( SURFACE(this),
392 (double)w
/(double)width(),
393 (double)h
/(double)height() );
395 SDL_FreeSurface( SURFACE(this) );
402 void Canvas::clear( const Rect
& r
)
405 SDL_Rect srcRect
= { r
.tl
.x
, r
.tl
.y
, r
.br
.x
-r
.tl
.x
+1, r
.br
.y
-r
.tl
.y
+1 };
406 SDL_BlitSurface( SURFACE(m_bgImage
), &srcRect
, SURFACE(this), &srcRect
);
408 drawRect( r
, m_bgColour
);
412 void Canvas::drawImage( Canvas
*canvas
, int x
, int y
)
414 SDL_Rect dest
= { x
, y
, 0, 0 };
415 SDL_BlitSurface( SURFACE(canvas
), NULL
, SURFACE(this), &dest
);
418 void Canvas::drawPixel( int x
, int y
, int c
)
422 bpp
= SURFACE(this)->format
->BytesPerPixel
;
423 ofs
= SURFACE(this)->pitch
*y
;
424 char* row
= (char*)SURFACE(this)->pixels
+ ofs
;
426 SDL_LockSurface(SURFACE(this));
428 case 2: ((Uint16
*)row
)[x
] = c
; break;
429 case 4: ((Uint32
*)row
)[x
] = c
; break;
431 SDL_UnlockSurface(SURFACE(this));
434 int Canvas::readPixel( int x
, int y
) const
439 bpp
= SURFACE(this)->format
->BytesPerPixel
;
440 ofs
= SURFACE(this)->pitch
*y
;
441 char* row
= (char*)SURFACE(this)->pixels
+ ofs
;
443 SDL_LockSurface(SURFACE(this));
445 case 2: c
= ((Uint16
*)row
)[x
]; break;
446 case 4: c
= ((Uint32
*)row
)[x
]; break;
449 SDL_UnlockSurface(SURFACE(this));
453 void Canvas::drawLine( int x1
, int y1
, int x2
, int y2
, int color
)
455 int lg_delta
, sh_delta
, cycle
, lg_step
, sh_step
;
458 lg_step
= Sgn(lg_delta
);
459 lg_delta
= Abs(lg_delta
);
460 sh_step
= Sgn(sh_delta
);
461 sh_delta
= Abs(sh_delta
);
462 if (sh_delta
< lg_delta
) {
463 cycle
= lg_delta
>> 1;
465 drawPixel( x1
, y1
, color
);
467 if (cycle
> lg_delta
) {
473 drawPixel( x1
, y1
, color
);
475 cycle
= sh_delta
>> 1;
477 drawPixel( x1
, y1
, color
);
479 if (cycle
> sh_delta
) {
485 drawPixel( x1
, y1
, color
);
488 void Canvas::drawPath( const Path
& path
, int color
, bool thick
)
490 // allow for thick lines in clipping
492 clip
.tl
.x
++; clip
.tl
.y
++;
493 clip
.br
.x
--; clip
.br
.y
--;
496 const int n
= path
.numPoints();
498 for ( ; i
<n
&& !clip
.contains( path
.point(i
) ); i
++ ) {
499 //skip clipped start pt
502 SDL_LockSurface(SURFACE(this));
504 // pt i-1 is guranteed to be inside clipping
505 const Vec2
& p2
= path
.point(i
);
506 if ( clip
.contains( p2
) ) {
507 const Vec2
& p1
= path
.point(i
-1);
508 switch ( SURFACE(this)->format
->BytesPerPixel
) {
511 renderLine
<Uint16
,3>( SURFACE(this)->pixels
,
512 SURFACE(this)->pitch
,
513 p1
.x
, p1
.y
, p2
.x
, p2
.y
, color
);
515 renderLine
<Uint16
,1>( SURFACE(this)->pixels
,
516 SURFACE(this)->pitch
,
517 p1
.x
, p1
.y
, p2
.x
, p2
.y
, color
);
522 renderLine
<Uint32
,3>( SURFACE(this)->pixels
,
523 SURFACE(this)->pitch
,
524 p1
.x
, p1
.y
, p2
.x
, p2
.y
, color
);
526 renderLine
<Uint32
,1>( SURFACE(this)->pixels
,
527 SURFACE(this)->pitch
,
528 p1
.x
, p1
.y
, p2
.x
, p2
.y
, color
);
533 for ( ; i
<n
&& !clip
.contains( path
.point(i
) ); i
++ ) {
534 //skip until we find a unclipped pt - this will be p1 next
539 SDL_UnlockSurface(SURFACE(this));
543 void Canvas::drawRect( int x
, int y
, int w
, int h
, int c
, bool fill
)
546 SDL_Rect r
= { x
, y
, w
, h
};
547 SDL_FillRect( SURFACE(this), &r
, c
);
549 SDL_Rect f
= { x
, y
, w
, h
};
551 r
=f
; r
.h
=1; SDL_FillRect( SURFACE(this), &r
, c
);
552 r
.y
+=f
.h
-1; SDL_FillRect( SURFACE(this), &r
, c
);
553 r
=f
; r
.w
=1; SDL_FillRect( SURFACE(this), &r
, c
);
554 r
.x
+=f
.w
-1; SDL_FillRect( SURFACE(this), &r
, c
);
558 void Canvas::previewCursor( int cursor_id
, int x
, int y
)
560 drawRect(x
-5, y
-5, 10, 10, 0, true);
562 SDL_Flip( SURFACE(this) );
565 void Canvas::drawRect( const Rect
& r
, int c
, bool fill
)
567 drawRect( r
.tl
.x
, r
.tl
.y
, r
.br
.x
-r
.tl
.x
, r
.br
.y
-r
.tl
.y
, c
, fill
);
572 Window::Window( int w
, int h
, const char* title
, const char* winclass
, bool fullscreen
)
576 snprintf(s
,80,"SDL_VIDEO_X11_WMCLASS=%s",winclass
);
580 SDL_WM_SetCaption( title
, title
);
583 m_state
= SDL_SetVideoMode( w
, h
, 16, SDL_SWSURFACE
);//SDL_FULLSCREEN);
584 SDL_WM_ToggleFullScreen( SURFACE(this) );
585 SDL_ShowCursor( SDL_DISABLE
);
587 m_state
= SDL_SetVideoMode( w
, h
, 32, SDL_SWSURFACE
| ((fullscreen
==true)?(SDL_FULLSCREEN
):(0)));
589 if ( SURFACE(this) == NULL
) {
590 throw "Unable to set video mode";
596 SDL_VERSION( &sys
.version
);
597 SDL_GetWMInfo( &sys
);
598 printf("X11 window =%08x\n",sys
.info
.x11
.window
);
599 printf("X11 fswindow =%08x\n",sys
.info
.x11
.fswindow
);
600 printf("X11 wmwindow =%08x\n",sys
.info
.x11
.wmwindow
);
602 uint32_t pid
= getpid();
603 XChangeProperty( sys
.info
.x11
.display
,
604 sys
.info
.x11
.wmwindow
,
605 XInternAtom (sys
.info
.x11
.display
, "_NET_WM_PID", False
),
606 XA_CARDINAL
, 32, PropModeReplace
,
607 (unsigned char*)&pid
, 1 );
612 void Window::update( const Rect
& r
)
614 if ( r
.tl
.x
< width() && r
.tl
.y
< height() ) {
615 int x1
= Max( 0, r
.tl
.x
);
616 int y1
= Max( 0, r
.tl
.y
);
617 int x2
= Min( width()-1, r
.br
.x
);
618 int y2
= Min( height()-1, r
.br
.y
);
619 int w
= Max( 0, x2
-x1
);
620 int h
= Max( 0, y2
-y1
);
621 if ( w
> 0 && h
> 0 ) {
622 SDL_UpdateRect( SURFACE(this), x1
, y1
, w
, h
);
630 SDL_VERSION( &sys
.version
);
631 SDL_GetWMInfo( &sys
);
633 #if !defined(WIN32) && !(defined(__APPLE__) && defined(__MACH__))
634 /* No X11 stuff on Windows and Mac OS X */
638 ev
.xclient
.type
= ClientMessage
;
639 ev
.xclient
.window
= sys
.info
.x11
.wmwindow
;
640 ev
.xclient
.message_type
= XInternAtom (sys
.info
.x11
.display
,
641 "_NET_ACTIVE_WINDOW", False
);
642 ev
.xclient
.format
= 32;
643 //all xewv.xclient.data==0 -> older spec?
645 XSendEvent (sys
.info
.x11
.display
,
646 DefaultRootWindow(sys
.info
.x11
.display
),
648 NoEventMask
, //SubstructureRedirectMask,
650 XSync( sys
.info
.x11
.display
, False
);
652 //XRaiseWindow( sys.info.x11.display, sys.info.x11.window );
656 void Window::setSubName( const char *sub
)
660 SDL_VERSION( &sys
.version
);
661 SDL_GetWMInfo( &sys
);
663 XChangeProperty( sys
.info
.x11
.display
,
664 sys
.info
.x11
.fswindow
,
665 XInternAtom (sys
.info
.x11
.display
, "_MB_WIN_SUB_NAME", False
),
666 XA_STRING
, 8, PropModeReplace
,
667 (unsigned char*)sub
, strlen(sub
) );
672 Image::Image( const char* file
, bool alpha
)
675 std::string
f( "data/" );
676 SDL_Surface
* img
= IMG_Load((f
+file
).c_str());
678 f
= std::string( DEFAULT_RESOURCE_PATH
"/" );
679 img
= IMG_Load((f
+file
).c_str());
682 printf("loaded image %s\n",(f
+file
).c_str());
684 m_state
= SDL_DisplayFormatAlpha( img
);
686 m_state
= SDL_DisplayFormat( img
);
687 // SDL_SetColorKey( SURFACE(this),
688 // SDL_SRCCOLORKEY|SDL_RLEACCEL,
689 // makeColour( 0x00ff00ff ) );
692 SDL_FreeSurface( img
);
694 printf("warning image %s not converted to display format\n",(f
+file
).c_str());
698 throw "image not found";
705 int Canvas::writeBMP( const char* filename
) const
708 unsigned short int type
; /* Magic identifier */
709 unsigned int size
; /* File size in bytes */
710 unsigned short int reserved1
, reserved2
;
711 unsigned int offset
; /* Offset to image data, bytes */
715 unsigned int size
; /* Header size in bytes */
716 int width
,height
; /* Width and height of image */
717 unsigned short int planes
; /* Number of colour planes */
718 unsigned short int bits
; /* Bits per pixel */
719 unsigned int compression
; /* Compression type */
720 unsigned int imagesize
; /* Image size in bytes */
721 int xresolution
,yresolution
; /* Pixels per meter */
722 unsigned int ncolours
; /* Number of colours */
723 unsigned int importantcolours
; /* Important colours */
728 BMPHEADER head
= { 'B'|('M'<<8), 14+40+w
*h
*3, 0, 0, 0 };
729 BMPINFOHEADER info
= { 40, w
, h
, 1, 24, 0, w
*h
*3, 100, 100, 0, 0 };
731 FILE *f
= fopen( filename
, "wb" );
734 bpp
= SURFACE(this)->format
->BytesPerPixel
;
736 fwrite( &head
, 14, 1, f
);
737 fwrite( &info
, 40, 1, f
);
738 for ( int y
=h
-1; y
>=0; y
-- ) {
739 for ( int x
=0; x
<w
; x
++ ) {
740 int p
= readPixel( x
, y
);
742 p
= R16G16B16_TO_RGB888( R16(p
), G16(p
), B16(p
) );
744 fwrite( &p
, 3, 1, f
);