Add pointlog2svg utility for the thesis
[numtypysics.git] / Canvas.cpp
blob610d9f05b34afeeaf30d115cef36dce5941008e4
1 /*
2 * This file is part of NumptyPhysics
3 * Copyright (C) 2008 Tim Edmonds
4 *
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.
9 *
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.
17 #include <string>
18 #include "Common.h"
19 #include "Config.h"
20 #include "Canvas.h"
21 #include "Path.h"
23 #include <SDL/SDL.h>
24 #include <SDL/SDL_image.h>
26 #define Window X11Window //oops
27 #define Font X11Font //oops
28 #include <SDL/SDL_syswm.h>
29 #ifndef WIN32
30 #include <X11/X.h>
31 #include <X11/Xlib.h>
32 #endif
33 #undef Window
35 // zoomer.cpp
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 )
77 { //565
78 p = R16G16B16_TO_RGB565( a * cr + ia * R16(p),
79 a * cg + ia * G16(p),
80 a * cb + ia * B16(p) );
83 inline void AlphaBlend( Uint32& p, int cr, int cg, int cb, int a, int ia )
85 { //888
86 p = R16G16B16_TO_RGB888( a * cr + ia * R32(p),
87 a * cg + ia * G32(p),
88 a * cb + ia * B32(p) );
91 #define ALPHA_MAX 0xff
93 template <typename PIX, unsigned W>
94 struct AlphaBrush
96 int m_r, m_g, m_b, m_c;
97 inline AlphaBrush( PIX c )
99 m_c = 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;
105 int o=-W/2+1;
106 AlphaBlend( *(pix+o*step), m_r, m_g, m_b, a, ia );
107 for ( ; o<=W/2; o++ ) {
108 *(pix+o*step) = m_c;
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 )
120 m_c = 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 )
137 m_c = 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 );
144 *(pix) = m_c;
145 AlphaBlend( *(pix+step), m_r, m_g, m_b, ia, a );
151 template <typename PIX, unsigned THICK>
152 inline void renderLine( void *buf,
153 int byteStride,
154 int x1, int y1, int x2, int y2,
155 PIX color )
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 );
163 lg_delta = x2 - x1;
164 sh_delta = y2 - y1;
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;
179 while ( count-- ) {
180 brush.ink( pix, pixStride, alpha );
181 cycle += sh_delta;
182 alpha += alpha_step;
183 pix += lg_step;
184 if (cycle > lg_delta) {
185 cycle -= lg_delta;
186 alpha = alpha_reset;
187 pix += pixStride;
190 } else {
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;
196 while ( count-- ) {
197 brush.ink( pix, 1, alpha );
198 cycle += lg_delta;
199 alpha += alpha_step;
200 pix += pixStride;
201 if (cycle > sh_delta) {
202 cycle -= sh_delta;
203 alpha = alpha_reset;
204 pix += lg_step;
211 #define SURFACE(cANVASpTR) ((SDL_Surface*)((cANVASpTR)->m_state))
213 Canvas::Canvas( int w, int h )
214 : m_state(NULL),
215 m_bgColour(0),
216 m_bgImage(NULL)
218 switch (SDL_GetVideoInfo()->vfmt->BitsPerPixel) {
219 case 16:
220 case 32:
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 );
227 break;
228 default:
229 // eg: dummy vid driver reports 8bpp
230 m_state = SDL_CreateRGBSurface( SDL_SWSURFACE, w, h, 32,
231 0xFF0000, 0x00FF00, 0x0000FF, 0xFF000000 );
232 break;
234 resetClip();
238 Canvas::Canvas( State state )
239 : m_state(state),
240 m_bgColour(0),
241 m_bgImage(NULL)
243 resetClip();
246 Canvas::~Canvas()
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()
273 if ( m_state ) {
274 setClip( 0, 0, width(), height() );
275 } else {
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 )
287 m_bgColour = c;
290 void Canvas::setBackground( Canvas* bg )
292 m_bgImage = bg;
295 void Canvas::clear()
297 if ( m_bgImage ) {
298 SDL_BlitSurface( SURFACE(m_bgImage), NULL, SURFACE(this), NULL );
299 } else {
300 SDL_FillRect( SURFACE(this), NULL, m_bgColour );
304 void Canvas::fade( const Rect& rr )
306 Uint32 bpp;
307 Rect r = rr;
308 r.clipTo( m_clip );
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));
317 switch ( bpp ) {
318 case 2:
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;
325 break;
326 case 4:
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;
333 break;
335 SDL_UnlockSurface(SURFACE(this));
338 Canvas* Canvas::scale( int factor ) const
340 Canvas *c = new Canvas( width()/factor, height()/factor );
341 if ( c ) {
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++ ) {
349 uint16 p = 0;
350 uint16_t *srow = (uint16_t*)SURFACE(this)->pixels
351 + (y*spitch+x)*factor;
352 for ( int yy=0;yy<factor;yy++ ) {
353 uint16 q = 0;
354 for ( int xx=0;xx<factor;xx++ ) {
355 q += (srow[xx]&MASK2LSB)>>2;
357 p += (q&MASK2LSB)>>2;
358 srow += spitch;
360 drow[x] = p;
362 drow += dpitch;
364 } else {
365 for ( int y=0;y<c->height();y++ ) {
366 for ( int x=0;x<c->width();x++ ) {
367 int r=0,g=0,b=0;
368 Uint8 rr,gg,bb;
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 );
373 r += rr;
374 g += gg;
375 b += bb;
378 int div = factor*factor;
379 c->drawPixel( x, y, makeColour(r/div,g/div,b/div) );
384 return c;
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() );
394 if ( s ) {
395 SDL_FreeSurface( SURFACE(this) );
396 m_state = s;
402 void Canvas::clear( const Rect& r )
404 if ( m_bgImage ) {
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 );
407 } else {
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 )
420 Uint32 bpp, ofs;
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));
427 switch ( bpp ) {
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
436 Uint32 bpp, ofs;
437 int c;
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));
444 switch ( bpp ) {
445 case 2: c = ((Uint16*)row)[x]; break;
446 case 4: c = ((Uint32*)row)[x]; break;
447 default: c=0; break;
449 SDL_UnlockSurface(SURFACE(this));
450 return c;
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;
456 lg_delta = x2 - x1;
457 sh_delta = y2 - y1;
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;
464 while (x1 != x2) {
465 drawPixel( x1, y1, color);
466 cycle += sh_delta;
467 if (cycle > lg_delta) {
468 cycle -= lg_delta;
469 y1 += sh_step;
471 x1 += lg_step;
473 drawPixel( x1, y1, color);
475 cycle = sh_delta >> 1;
476 while (y1 != y2) {
477 drawPixel( x1, y1, color);
478 cycle += lg_delta;
479 if (cycle > sh_delta) {
480 cycle -= sh_delta;
481 x1 += lg_step;
483 y1 += sh_step;
485 drawPixel( x1, y1, color);
488 void Canvas::drawPath( const Path& path, int color, bool thick )
490 // allow for thick lines in clipping
491 Rect clip = m_clip;
492 clip.tl.x++; clip.tl.y++;
493 clip.br.x--; clip.br.y--;
495 int i=0;
496 const int n = path.numPoints();
498 for ( ; i<n && !clip.contains( path.point(i) ); i++ ) {
499 //skip clipped start pt
501 i++;
502 SDL_LockSurface(SURFACE(this));
503 for ( ; i<n; i++ ) {
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 ) {
509 case 2:
510 if ( thick ) {
511 renderLine<Uint16,3>( SURFACE(this)->pixels,
512 SURFACE(this)->pitch,
513 p1.x, p1.y, p2.x, p2.y, color );
514 } else {
515 renderLine<Uint16,1>( SURFACE(this)->pixels,
516 SURFACE(this)->pitch,
517 p1.x, p1.y, p2.x, p2.y, color );
519 break;
520 case 4:
521 if ( thick ) {
522 renderLine<Uint32,3>( SURFACE(this)->pixels,
523 SURFACE(this)->pitch,
524 p1.x, p1.y, p2.x, p2.y, color );
525 } else {
526 renderLine<Uint32,1>( SURFACE(this)->pixels,
527 SURFACE(this)->pitch,
528 p1.x, p1.y, p2.x, p2.y, color );
530 break;
532 } else {
533 for ( ; i<n && !clip.contains( path.point(i) ); i++ ) {
534 //skip until we find a unclipped pt - this will be p1 next
535 //time around
539 SDL_UnlockSurface(SURFACE(this));
543 void Canvas::drawRect( int x, int y, int w, int h, int c, bool fill )
545 if ( fill ) {
546 SDL_Rect r = { x, y, w, h };
547 SDL_FillRect( SURFACE(this), &r, c );
548 } else {
549 SDL_Rect f = { x, y, w, h };
550 SDL_Rect r;
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 )
574 if ( winclass ) {
575 char s[80];
576 snprintf(s,80,"SDL_VIDEO_X11_WMCLASS=%s",winclass);
577 putenv(s);
579 if ( title ) {
580 SDL_WM_SetCaption( title, title );
582 #ifdef USE_HILDON
583 m_state = SDL_SetVideoMode( w, h, 16, SDL_SWSURFACE);//SDL_FULLSCREEN);
584 SDL_WM_ToggleFullScreen( SURFACE(this) );
585 SDL_ShowCursor( SDL_DISABLE );
586 #else
587 m_state = SDL_SetVideoMode( w, h, 32, SDL_SWSURFACE | ((fullscreen==true)?(SDL_FULLSCREEN):(0)));
588 #endif
589 if ( SURFACE(this) == NULL ) {
590 throw "Unable to set video mode";
592 resetClip();
594 #ifdef USE_HILDON
595 SDL_SysWMinfo sys;
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 );
608 #endif
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 );
627 void Window::raise()
629 SDL_SysWMinfo sys;
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 */
636 // take focus...
637 XEvent ev = { 0 };
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),
647 False,
648 NoEventMask, //SubstructureRedirectMask,
649 &ev);
650 XSync( sys.info.x11.display, False );
651 #endif
652 //XRaiseWindow( sys.info.x11.display, sys.info.x11.window );
656 void Window::setSubName( const char *sub )
658 #ifdef USE_HILDON
659 SDL_SysWMinfo sys;
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) );
668 #endif
672 Image::Image( const char* file, bool alpha )
674 alpha = false;
675 std::string f( "data/" );
676 SDL_Surface* img = IMG_Load((f+file).c_str());
677 if ( !img ) {
678 f = std::string( DEFAULT_RESOURCE_PATH "/" );
679 img = IMG_Load((f+file).c_str());
681 if ( img ) {
682 printf("loaded image %s\n",(f+file).c_str());
683 if ( alpha ) {
684 m_state = SDL_DisplayFormatAlpha( img );
685 } else {
686 m_state = SDL_DisplayFormat( img );
687 // SDL_SetColorKey( SURFACE(this),
688 // SDL_SRCCOLORKEY|SDL_RLEACCEL,
689 // makeColour( 0x00ff00ff ) );
691 if ( m_state ) {
692 SDL_FreeSurface( img );
693 } else {
694 printf("warning image %s not converted to display format\n",(f+file).c_str());
695 m_state = img;
697 } else {
698 throw "image not found";
700 resetClip();
705 int Canvas::writeBMP( const char* filename ) const
707 typedef struct {
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 */
712 } BMPHEADER;
714 typedef struct {
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 */
724 } BMPINFOHEADER;
726 int w = width();
727 int h = height();
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" );
732 if ( f ) {
733 Uint32 bpp;
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 );
741 if ( bpp==2 ) {
742 p = R16G16B16_TO_RGB888( R16(p), G16(p), B16(p) );
744 fwrite( &p, 3, 1, f );
747 fclose(f);
748 return 1;
750 return 0;