1 /*	SCCS Id: @(#)tileedit.cpp	3.3	1999/11/19	*/
2 /* Copyright (c) Warwick Allison, 1999. */
3 /* NetHack may be freely redistributed.  See license for details. */
4 /*
5 Build this little utility program if you want to use it to edit the tile
6 files.  Move tileedit.cpp and tileedit.h to ../../util, add the
7 3 lines below to the Makefile there and "make tileedit".
8 
9 tileedit: tileedit.cpp $(TEXT_IO)
10 	moc -o tileedit.moc tileedit.h
11 	$(CC) -o tileedit -I../include -I$(QTDIR)/include -L$(QTDIR)/lib tileedit.cpp $(TEXT_IO) -lqt
12 */
13 
14 
15 #include "tileedit.h"
16 #include <qapplication.h>
17 #include <qmainwindow.h>
18 #include <qkeycode.h>
19 #include <qpopupmenu.h>
20 #include <qmenubar.h>
21 #include <qpainter.h>
22 #include <qstatusbar.h>
23 #include <qhbox.h>
24 #include <qlabel.h>
25 
26 extern "C" {
27 #include "config.h"
28 #include "tile.h"
29 extern const char *FDECL(tilename, (int, int));
30 }
31 
32 #define TILES_ACROSS 20
33 
TilePickerTab(const char * basename,int i,QWidget * parent)34 TilePickerTab::TilePickerTab(const char* basename, int i, QWidget* parent) :
35     QWidget(parent)
36 {
37     id = i;
38     filename = basename;
39     filename += ".txt";
40     num = 0;
41     int index = 0;
42     for (int real=0; real<2; real++) {
43 	if ( real ) {
44 	    image.create( TILES_ACROSS*TILE_X,
45 		((num+TILES_ACROSS-1)/TILES_ACROSS)*TILE_Y, 32 );
46 	}
47 	if ( !fopen_text_file(filename.latin1(), RDTMODE) ) {
48 	    // XXX handle better
49 	    exit(1);
50 	}
51 	pixel p[TILE_Y][TILE_X];
52 	while ( read_text_tile(p) ) {
53 	    if ( real ) {
54 		int ox = (index%TILES_ACROSS)*TILE_X;
55 		int oy = (index/TILES_ACROSS)*TILE_Y;
56 		for ( int y=0; y<TILE_Y; y++ ) {
57 		    QRgb* rgb = ((QRgb*)image.scanLine(oy+y)) + ox;
58 		    for ( int x=0; x<TILE_X; x++ ) {
59 			*rgb++ = qRgb(p[y][x].r, p[y][x].g, p[y][x].b);
60 		    }
61 		}
62 		index++;
63 	    } else {
64 		// Just count...
65 		num++;
66 	    }
67 	}
68 	fclose_text_file();
69     }
70     image = image.convertDepth( 8, AvoidDither );
71     pixmap.convertFromImage( image );
72 }
73 
save()74 bool TilePickerTab::save()
75 {
76     if ( !fopen_text_file(filename.latin1(), WRTMODE) ) {
77 	// XXX handle better
78 	exit(1);
79     }
80     pixel p[TILE_Y][TILE_X];
81     for ( int index=0; index < num; index++ ) {
82 	int ox = (index%TILES_ACROSS)*TILE_X;
83 	int oy = (index/TILES_ACROSS)*TILE_Y;
84 	for ( int y=0; y<TILE_Y; y++ ) {
85 	    uchar* c = image.scanLine(oy+y) + ox;
86 	    for ( int x=0; x<TILE_X; x++ ) {
87 		QRgb rgb = image.color(*c++);
88 		p[y][x].r = qRed(rgb);
89 		p[y][x].g = qGreen(rgb);
90 		p[y][x].b = qBlue(rgb);
91 	    }
92 	}
93 	write_text_tile(p);
94     }
95     fclose_text_file();
96 }
97 
mousePressEvent(QMouseEvent * e)98 void TilePickerTab::mousePressEvent(QMouseEvent* e)
99 {
100     int ox = e->x()-e->x()%TILE_X;
101     int oy = e->y()-e->y()%TILE_Y;
102     QImage subimage = image.copy(ox,oy,TILE_X,TILE_Y);
103     if ( e->button() == RightButton ) {
104 	setCurrent(subimage);
105     } else {
106 	last_pick = ox/TILE_X + oy/TILE_Y*TILES_ACROSS;
107     }
108     emit pick(subimage);
109     emit pickName(tilename(id, last_pick));
110 }
111 
setCurrent(const QImage & i)112 void TilePickerTab::setCurrent(const QImage& i)
113 {
114     int ox = last_pick%TILES_ACROSS * TILE_X;
115     int oy = last_pick/TILES_ACROSS * TILE_Y;
116     bitBlt( &image, ox, oy, &i );
117     bitBlt( &pixmap, ox, oy, &i );
118     repaint( ox, oy, TILE_X, TILE_Y, FALSE );
119 }
120 
sizeHint() const121 QSize TilePickerTab::sizeHint() const
122 {
123     return pixmap.size();
124 }
125 
paintEvent(QPaintEvent *)126 void TilePickerTab::paintEvent( QPaintEvent* )
127 {
128     QPainter p(this);
129     p.drawPixmap(0,0,pixmap);
130 }
131 
132 static struct {
133     const char* name;
134     TilePickerTab* tab;
135 } tileset[] = {
136     { "monsters", 0 },
137     { "objects", 0 },
138     { "other", 0 },
139     { 0 }
140 };
141 
TilePicker(QWidget * parent)142 TilePicker::TilePicker(QWidget* parent) :
143     QTabWidget(parent)
144 {
145     for (int i=0; tileset[i].name; i++) {
146 	QString tabname = tileset[i].name;
147 	tabname[0] = tabname[0].upper();
148 	tileset[i].tab = new TilePickerTab(tileset[i].name,i+1,this);
149 	addTab( tileset[i].tab, tabname );
150 	connect( tileset[i].tab, SIGNAL(pick(const QImage&)),
151 		 this, SIGNAL(pick(const QImage&)) );
152 	connect( tileset[i].tab, SIGNAL(pickName(const QString&)),
153 		 this, SIGNAL(pickName(const QString&)) );
154     }
155 }
156 
setCurrent(const QImage & i)157 void TilePicker::setCurrent(const QImage& i)
158 {
159     ((TilePickerTab*)currentPage())->setCurrent(i);
160 }
161 
save()162 void TilePicker::save()
163 {
164     for (int i=0; tileset[i].tab; i++) {
165 	tileset[i].tab->save();
166     }
167 }
168 
TrivialTileEditor(QWidget * parent)169 TrivialTileEditor::TrivialTileEditor( QWidget* parent ) :
170     QWidget(parent)
171 {
172 }
173 
image() const174 const QImage& TrivialTileEditor::image() const
175 {
176     return img;
177 }
178 
setColor(QRgb rgb)179 void TrivialTileEditor::setColor( QRgb rgb )
180 {
181     pen = rgb;
182     for (penpixel = 0;
183 	    penpixel<img.numColors()-1 && img.color(penpixel)!=pen.rgb();
184 	    penpixel++)
185 	continue;
186 }
187 
setImage(const QImage & i)188 void TrivialTileEditor::setImage( const QImage& i )
189 {
190     img = i;
191     setColor(pen.rgb()); // update penpixel
192     repaint(FALSE);
193 }
194 
paintEvent(QPaintEvent * e)195 void TrivialTileEditor::paintEvent( QPaintEvent* e )
196 {
197     QRect r = e->rect();
198     QPoint tl = imagePoint(r.topLeft());
199     QPoint br = imagePoint(r.bottomRight());
200     r = QRect(tl,br).intersect(img.rect());
201     QPainter painter(this);
202     for (int y=r.top(); y<=r.bottom(); y++) {
203 	for (int x=r.left(); x<=r.right(); x++) {
204 	    paintPoint(painter,QPoint(x,y));
205 	}
206     }
207 }
208 
paintPoint(QPainter & painter,QPoint p)209 void TrivialTileEditor::paintPoint(QPainter& painter, QPoint p)
210 {
211     QPoint p1 = screenPoint(p);
212     QPoint p2 = screenPoint(p+QPoint(1,1));
213     QColor c = img.color(img.scanLine(p.y())[p.x()]);
214     painter.fillRect(QRect(p1,p2-QPoint(1,1)), c);
215 }
216 
mousePressEvent(QMouseEvent * e)217 void TrivialTileEditor::mousePressEvent(QMouseEvent* e)
218 {
219     QPoint p = imagePoint(e->pos());
220     uchar& pixel = img.scanLine(p.y())[p.x()];
221     if ( e->button() == LeftButton ) {
222 	pixel = penpixel;
223 	QPainter painter(this);
224 	paintPoint(painter,p);
225     } else if ( e->button() == RightButton ) {
226 	emit pick( img.color(pixel) );
227     } else if ( e->button() == MidButton ) {
228 	QPainter painter(this);
229 	fill(painter,p,pixel);
230     }
231 }
232 
fill(QPainter & painter,QPoint p,uchar from)233 void TrivialTileEditor::fill(QPainter& painter, QPoint p, uchar from)
234 {
235     if ( img.rect().contains(p) ) {
236 	uchar& pixel = img.scanLine(p.y())[p.x()];
237 	if ( pixel == from ) {
238 	    pixel = penpixel;
239 	    paintPoint(painter,p);
240 	    fill(painter, p+QPoint(-1,0), from);
241 	    fill(painter, p+QPoint(+1,0), from);
242 	    fill(painter, p+QPoint(0,-1), from);
243 	    fill(painter, p+QPoint(0,+1), from);
244 	}
245     }
246 }
247 
mouseReleaseEvent(QMouseEvent * e)248 void TrivialTileEditor::mouseReleaseEvent(QMouseEvent* e)
249 {
250     emit edited(image());
251 }
252 
mouseMoveEvent(QMouseEvent * e)253 void TrivialTileEditor::mouseMoveEvent(QMouseEvent* e)
254 {
255     QPoint p = imagePoint(e->pos());
256     uchar& pixel = img.scanLine(p.y())[p.x()];
257     pixel = penpixel;
258     QPainter painter(this);
259     paintPoint(painter,p);
260 }
261 
imagePoint(QPoint p) const262 QPoint TrivialTileEditor::imagePoint(QPoint p) const
263 {
264     return QPoint(p.x()*TILE_X/width(), p.y()*TILE_Y/height());
265 }
266 
screenPoint(QPoint p) const267 QPoint TrivialTileEditor::screenPoint(QPoint p) const
268 {
269     return QPoint(p.x()*width()/TILE_X, p.y()*height()/TILE_Y);
270 }
271 
sizePolicy() const272 QSizePolicy TrivialTileEditor::sizePolicy() const
273 {
274     return QSizePolicy( QSizePolicy::Expanding, QSizePolicy::Expanding, TRUE );
275 }
276 
sizeHint() const277 QSize TrivialTileEditor::sizeHint() const
278 {
279     return sizeForWidth(-1);
280 }
281 
sizeForWidth(int w) const282 QSize TrivialTileEditor::sizeForWidth(int w) const
283 {
284     if ( w < 0 )
285 	return QSize(TILE_X*32,TILE_Y*32);
286     else
287 	return QSize(w,w*TILE_Y/TILE_X);
288 }
289 
290 
TilePalette(QWidget * parent)291 TilePalette::TilePalette( QWidget* parent ) :
292     QWidget(parent)
293 {
294     num = 0;
295     rgb = 0;
296 }
297 
~TilePalette()298 TilePalette::~TilePalette()
299 {
300     delete rgb;
301 }
302 
setFromImage(const QImage & i)303 void TilePalette::setFromImage( const QImage& i )
304 {
305     num = i.numColors();
306     rgb = new QRgb[num];
307     memcpy(rgb, i.colorTable(), num*sizeof(QRgb));
308     repaint(FALSE);
309 }
310 
setColor(QRgb c)311 void TilePalette::setColor(QRgb c)
312 {
313     for (int i=0; i<num; i++)
314 	if ( c == rgb[i] ) {
315 	    emit pick(c);
316 	    return;
317 	}
318 }
319 
sizePolicy() const320 QSizePolicy TilePalette::sizePolicy() const
321 {
322     return QSizePolicy( QSizePolicy::Minimum, QSizePolicy::Minimum, FALSE );
323 }
324 
sizeHint() const325 QSize TilePalette::sizeHint() const
326 {
327     return QSize(num*16,16);
328 }
329 
paintEvent(QPaintEvent *)330 void TilePalette::paintEvent( QPaintEvent* )
331 {
332     QPainter p(this);
333     for (int i=0; i<num; i++) {
334 	int x1 = width()*i/num;
335 	int x2 = width()*(i+1)/num;
336 	p.fillRect(x1,0,x2-x1,height(),QColor(rgb[i]));
337     }
338 }
339 
mousePressEvent(QMouseEvent * e)340 void TilePalette::mousePressEvent(QMouseEvent* e)
341 {
342     int c = e->x()*num/width();
343     emit pick(rgb[c]);
344 }
345 
TileEditor(QWidget * parent)346 TileEditor::TileEditor(QWidget* parent) :
347     QVBox(parent),
348     editor(this),
349     palette(this)
350 {
351     connect( &palette, SIGNAL(pick(QRgb)),
352 	     &editor, SLOT(setColor(QRgb)) );
353     connect( &editor, SIGNAL(pick(QRgb)),
354 	     &palette, SLOT(setColor(QRgb)) );
355     connect( &editor, SIGNAL(edited(const QImage&)),
356 	     this, SIGNAL(edited(const QImage&)) );
357 }
358 
edit(const QImage & i)359 void TileEditor::edit(const QImage& i)
360 {
361     editor.setImage(i);
362     palette.setFromImage(i);
363 }
364 
image() const365 const QImage& TileEditor::image() const
366 {
367     return editor.image();
368 }
369 
370 class Main : public QMainWindow {
371 public:
Main()372     Main() :
373 	central(this),
374 	editor(&central),
375 	picker(&central)
376     {
377 	QPopupMenu* file = new QPopupMenu(menuBar());
378 	file->insertItem("&Save", &picker, SLOT(save()), CTRL+Key_S);
379 	file->insertSeparator();
380 	file->insertItem("&Exit", qApp, SLOT(quit()), CTRL+Key_Q);
381 	menuBar()->insertItem("&File", file);
382 
383 	connect( &picker, SIGNAL(pick(const QImage&)),
384 		 &editor, SLOT(edit(const QImage&)) );
385 	connect( &picker, SIGNAL(pickName(const QString&)),
386 		 statusBar(), SLOT(message(const QString&)) );
387 	connect( &editor, SIGNAL(edited(const QImage&)),
388 		 &picker, SLOT(setCurrent(const QImage&)) );
389 
390 	setCentralWidget(&central);
391     }
392 
393 private:
394     QHBox central;
395     TileEditor editor;
396     TilePicker picker;
397 };
398 
main(int argc,char ** argv)399 main(int argc, char** argv)
400 {
401     QApplication app(argc,argv);
402     Main m;
403     app.setMainWidget(&m);
404     m.show();
405     return app.exec();
406 }
407 
408 #include "tileedit.moc"
409