source: trunk/examples/showimg/showimg.cpp@ 16

Last change on this file since 16 was 2, checked in by dmik, 20 years ago

Imported xplatform parts of the official release 3.3.1 from Trolltech

  • Property svn:keywords set to Id
File size: 19.5 KB
Line 
1/****************************************************************************
2** $Id: showimg.cpp 2 2005-11-16 15:49:26Z dmik $
3**
4** Copyright (C) 1992-2000 Trolltech AS. All rights reserved.
5**
6** This file is part of an example program for Qt. This example
7** program may be used, distributed and modified without limitation.
8**
9*****************************************************************************/
10
11#include "showimg.h"
12#include "imagetexteditor.h"
13#include <qmenubar.h>
14#include <qfiledialog.h>
15#include <qmessagebox.h>
16#include <qpopupmenu.h>
17#include <qlabel.h>
18#include <qpainter.h>
19#include <qapplication.h>
20#include <qclipboard.h>
21
22
23/*
24 In the constructor, we just pass the standard parameters on to
25 QWidget.
26
27 The menu uses a single slot to simplify the process of adding
28 more items to the options menu.
29*/
30ImageViewer::ImageViewer( QWidget *parent, const char *name, int wFlags )
31 : QWidget( parent, name, wFlags ),
32 conversion_flags( PreferDither ),
33 helpmsg( 0 )
34{
35 pickx = -1;
36 picky = -1;
37 clickx = -1;
38 clicky = -1;
39 alloc_context = 0;
40
41 menubar = new QMenuBar(this);
42 menubar->setSeparator( QMenuBar::InWindowsStyle );
43
44 QStrList fmt = QImage::outputFormats();
45 saveimage = new QPopupMenu( menubar );
46 savepixmap = new QPopupMenu( menubar );
47 for (const char* f = fmt.first(); f; f = fmt.next()) {
48 saveimage->insertItem( f );
49 savepixmap->insertItem( f );
50 }
51 connect( saveimage, SIGNAL(activated(int)), this, SLOT(saveImage(int)) );
52 connect( savepixmap, SIGNAL(activated(int)), this, SLOT(savePixmap(int)) );
53
54 file = new QPopupMenu( menubar );
55 menubar->insertItem( "&File", file );
56 file->insertItem( "&New window", this, SLOT(newWindow()), CTRL+Key_N );
57 file->insertItem( "&Open...", this, SLOT(openFile()), CTRL+Key_O );
58 si = file->insertItem( "Save image", saveimage );
59 sp = file->insertItem( "Save pixmap", savepixmap );
60 file->insertSeparator();
61 file->insertItem( "E&xit", qApp, SLOT(quit()), CTRL+Key_Q );
62
63 edit = new QPopupMenu( menubar );
64 menubar->insertItem( "&Edit", edit );
65 edit->insertItem("&Copy", this, SLOT(copy()), CTRL+Key_C);
66 edit->insertItem("&Paste", this, SLOT(paste()), CTRL+Key_V);
67 edit->insertSeparator();
68 edit->insertItem("&Horizontal flip", this, SLOT(hFlip()), ALT+Key_H);
69 edit->insertItem("&Vertical flip", this, SLOT(vFlip()), ALT+Key_V);
70 edit->insertItem("&Rotate 180", this, SLOT(rot180()), ALT+Key_R);
71 edit->insertSeparator();
72 edit->insertItem("&Text...", this, SLOT(editText()));
73 edit->insertSeparator();
74 t1 = edit->insertItem( "Convert to &1 bit", this, SLOT(to1Bit()) );
75 t8 = edit->insertItem( "Convert to &8 bit", this, SLOT(to8Bit()) );
76 t32 = edit->insertItem( "Convert to &32 bit", this, SLOT(to32Bit()) );
77
78 options = new QPopupMenu( menubar );
79 menubar->insertItem( "&Options", options );
80 ac = options->insertItem( "AutoColor" );
81 co = options->insertItem( "ColorOnly" );
82 mo = options->insertItem( "MonoOnly" );
83 options->insertSeparator();
84 fd = options->insertItem( "DiffuseDither" );
85 bd = options->insertItem( "OrderedDither" );
86 td = options->insertItem( "ThresholdDither" );
87 options->insertSeparator();
88 ta = options->insertItem( "ThresholdAlphaDither" );
89 ba = options->insertItem( "OrderedAlphaDither" );
90 fa = options->insertItem( "DiffuseAlphaDither" );
91 options->insertSeparator();
92 ad = options->insertItem( "PreferDither" );
93 dd = options->insertItem( "AvoidDither" );
94 options->insertSeparator();
95 ss = options->insertItem( "Smooth scaling" );
96 cc = options->insertItem( "Use color context" );
97 if ( QApplication::colorSpec() == QApplication::ManyColor )
98 options->setItemEnabled( cc, FALSE );
99 options->setCheckable( TRUE );
100 setMenuItemFlags();
101
102 menubar->insertSeparator();
103
104 QPopupMenu* help = new QPopupMenu( menubar );
105 menubar->insertItem( "&Help", help );
106 help->insertItem( "Help!", this, SLOT(giveHelp()), CTRL+Key_H );
107
108 connect( options, SIGNAL(activated(int)), this, SLOT(doOption(int)) );
109
110 status = new QLabel(this);
111 status->setFrameStyle( QFrame::WinPanel | QFrame::Sunken );
112 status->setFixedHeight( fontMetrics().height() + 4 );
113
114 setMouseTracking( TRUE );
115}
116
117ImageViewer::~ImageViewer()
118{
119 if ( alloc_context )
120 QColor::destroyAllocContext( alloc_context );
121 if ( other == this )
122 other = 0;
123}
124
125/*
126 This function modifies the conversion_flags when an options menu item
127 is selected, then ensures all menu items are up to date, and reconverts
128 the image if possibly necessary.
129*/
130void ImageViewer::doOption(int item)
131{
132 if ( item == ss || item == cc ) {
133 // Toggle
134 bool newbool = !options->isItemChecked(item);
135 options->setItemChecked(item, newbool);
136 // And reconvert...
137 reconvertImage();
138 repaint(image.hasAlphaBuffer()); // show image in widget
139 return;
140 }
141
142 if ( options->isItemChecked( item ) ) return; // They are all radio buttons
143
144 int ocf = conversion_flags;
145
146 if ( item == ac ) {
147 conversion_flags = ( conversion_flags & ~ColorMode_Mask ) | AutoColor;
148 } else if ( item == co ) {
149 conversion_flags = ( conversion_flags & ~ColorMode_Mask ) | ColorOnly;
150 } else if ( item == mo ) {
151 conversion_flags = ( conversion_flags & ~ColorMode_Mask ) | MonoOnly;
152 } else if ( item == fd ) {
153 conversion_flags = ( conversion_flags & ~Dither_Mask ) | DiffuseDither;
154 } else if ( item == bd ) {
155 conversion_flags = ( conversion_flags & ~Dither_Mask ) | OrderedDither;
156 } else if ( item == td ) {
157 conversion_flags = ( conversion_flags & ~Dither_Mask ) | ThresholdDither;
158 } else if ( item == ta ) {
159 conversion_flags = ( conversion_flags & ~AlphaDither_Mask ) | ThresholdAlphaDither;
160 } else if ( item == fa ) {
161 conversion_flags = ( conversion_flags & ~AlphaDither_Mask ) | DiffuseAlphaDither;
162 } else if ( item == ba ) {
163 conversion_flags = ( conversion_flags & ~AlphaDither_Mask ) | OrderedAlphaDither;
164 } else if ( item == ad ) {
165 conversion_flags = ( conversion_flags & ~DitherMode_Mask ) | PreferDither;
166 } else if ( item == dd ) {
167 conversion_flags = ( conversion_flags & ~DitherMode_Mask ) | AvoidDither;
168 }
169
170 if ( ocf != conversion_flags ) {
171 setMenuItemFlags();
172 // And reconvert...
173 reconvertImage();
174 repaint(image.hasAlphaBuffer()); // show image in widget
175 }
176}
177
178/*
179 Set the options menu to reflect the conversion_flags value.
180*/
181void ImageViewer::setMenuItemFlags()
182{
183 // File
184 bool valid_image = pm.size() != QSize( 0, 0 );
185 file->setItemEnabled( si, valid_image );
186 file->setItemEnabled( sp, valid_image );
187
188 // Edit
189 edit->setItemEnabled( t1, image.depth() != 1 );
190 edit->setItemEnabled( t8, image.depth() != 8 );
191 edit->setItemEnabled( t32, image.depth() != 32 );
192
193 // Options
194 bool may_need_color_dithering =
195 !valid_image
196 || image.depth() == 32 && QPixmap::defaultDepth() < 24;
197 bool may_need_dithering = may_need_color_dithering
198 || image.depth() > 1 && options->isItemChecked(mo)
199 || image.depth() > 1 && QPixmap::defaultDepth() == 1;
200 bool has_alpha_mask = !valid_image || image.hasAlphaBuffer();
201
202 options->setItemEnabled( fd, may_need_dithering );
203 options->setItemEnabled( bd, may_need_dithering );
204 options->setItemEnabled( td, may_need_dithering );
205
206 options->setItemEnabled( ta, has_alpha_mask );
207 options->setItemEnabled( fa, has_alpha_mask );
208 options->setItemEnabled( ba, has_alpha_mask );
209
210 options->setItemEnabled( ad, may_need_color_dithering );
211 options->setItemEnabled( dd, may_need_color_dithering );
212
213 options->setItemChecked( ac, (conversion_flags & ColorMode_Mask) == AutoColor );
214 options->setItemChecked( co, (conversion_flags & ColorMode_Mask) == ColorOnly );
215 options->setItemChecked( mo, (conversion_flags & ColorMode_Mask) == MonoOnly );
216 options->setItemChecked( fd, (conversion_flags & Dither_Mask) == DiffuseDither );
217 options->setItemChecked( bd, (conversion_flags & Dither_Mask) == OrderedDither );
218 options->setItemChecked( td, (conversion_flags & Dither_Mask) == ThresholdDither );
219 options->setItemChecked( ta, (conversion_flags & AlphaDither_Mask) == ThresholdAlphaDither );
220 options->setItemChecked( fa, (conversion_flags & AlphaDither_Mask) == DiffuseAlphaDither );
221 options->setItemChecked( ba, (conversion_flags & AlphaDither_Mask) == OrderedAlphaDither );
222 options->setItemChecked( ad, (conversion_flags & DitherMode_Mask) == PreferDither );
223 options->setItemChecked( dd, (conversion_flags & DitherMode_Mask) == AvoidDither );
224}
225
226void ImageViewer::updateStatus()
227{
228 if ( pm.size() == QSize( 0, 0 ) ) {
229 if ( !filename.isEmpty() )
230 status->setText("Could not load image");
231 else
232 status->setText("No image - select Open from File menu.");
233 } else {
234 QString message, moremsg;
235 message.sprintf("%dx%d", image.width(), image.height());
236 if ( pm.size() != pmScaled.size() ) {
237 moremsg.sprintf(" [%dx%d]", pmScaled.width(),
238 pmScaled.height());
239 message += moremsg;
240 }
241 moremsg.sprintf(", %d bits ", image.depth());
242 message += moremsg;
243 if (image.valid(pickx,picky)) {
244 moremsg.sprintf("(%d,%d)=#%0*x ",
245 pickx, picky,
246 image.hasAlphaBuffer() ? 8 : 6,
247 image.pixel(pickx,picky));
248 message += moremsg;
249 }
250 if ( image.numColors() > 0 ) {
251 if (image.valid(pickx,picky)) {
252 moremsg.sprintf(", %d/%d colors", image.pixelIndex(pickx,picky),
253 image.numColors());
254 } else {
255 moremsg.sprintf(", %d colors", image.numColors());
256 }
257 message += moremsg;
258 }
259 if ( image.hasAlphaBuffer() ) {
260 if ( image.depth() == 8 ) {
261 int i;
262 bool alpha[256];
263 int nalpha=0;
264
265 for (i=0; i<256; i++)
266 alpha[i] = FALSE;
267
268 for (i=0; i<image.numColors(); i++) {
269 int alevel = image.color(i) >> 24;
270 if (!alpha[alevel]) {
271 alpha[alevel] = TRUE;
272 nalpha++;
273 }
274 }
275 moremsg.sprintf(", %d alpha levels", nalpha);
276 } else {
277 // Too many pixels to bother counting.
278 moremsg = ", 8-bit alpha channel";
279 }
280 message += moremsg;
281 }
282 status->setText(message);
283 }
284}
285
286/*
287 This function saves the image.
288*/
289void ImageViewer::saveImage( int item )
290{
291 const char* fmt = saveimage->text(item);
292 QString savefilename = QFileDialog::getSaveFileName(QString::null, QString::null,
293 this, filename);
294 if ( !savefilename.isEmpty() )
295 if ( !image.save( savefilename, fmt ) )
296 QMessageBox::warning( this, "Save failed", "Error saving file" );
297}
298
299/*
300 This function saves the converted image.
301*/
302void ImageViewer::savePixmap( int item )
303{
304 const char* fmt = savepixmap->text(item);
305 QString savefilename = QFileDialog::getSaveFileName(QString::null,
306 QString::null, this, filename);
307 if ( !savefilename.isEmpty() )
308 if ( !pmScaled.save( savefilename, fmt ) )
309 QMessageBox::warning( this, "Save failed", "Error saving file" );
310}
311
312
313void ImageViewer::newWindow()
314{
315 ImageViewer* that = new ImageViewer(0, "new window", WDestructiveClose);
316 that->options->setItemChecked( that->cc, useColorContext() );
317 that->show();
318}
319
320/*
321 This function is the slot for processing the Open menu item.
322*/
323void ImageViewer::openFile()
324{
325 QString newfilename = QFileDialog::getOpenFileName( QString::null,
326 QString::null,
327 this );
328 if ( !newfilename.isEmpty() ) {
329 loadImage( newfilename ) ;
330 repaint(); // show image in widget
331 }
332}
333
334/*
335 This function loads an image from a file and resizes the widget to
336 exactly fit the image size. If the file was not found or the image
337 format was unknown it will resize the widget to fit the errorText
338 message (see above) displayed in the current font.
339
340 Returns TRUE if the image was successfully loaded.
341*/
342
343bool ImageViewer::loadImage( const QString& fileName )
344{
345 filename = fileName;
346 bool ok = FALSE;
347 if ( !filename.isEmpty() ) {
348 QApplication::setOverrideCursor( waitCursor ); // this might take time
349 ok = image.load(filename, 0);
350 pickx = -1;
351 clickx = -1;
352 if ( ok )
353 ok = reconvertImage();
354 if ( ok ) {
355 setCaption( filename ); // set window caption
356 int w = pm.width();
357 int h = pm.height();
358
359 const int reasonable_width = 128;
360 if ( w < reasonable_width ) {
361 // Integer scale up to something reasonable
362 int multiply = ( reasonable_width + w - 1 ) / w;
363 w *= multiply;
364 h *= multiply;
365 }
366
367 h += menubar->heightForWidth(w) + status->height();
368 resize( w, h ); // we resize to fit image
369 } else {
370 pm.resize(0,0); // couldn't load image
371 update();
372 }
373 QApplication::restoreOverrideCursor(); // restore original cursor
374 }
375 updateStatus();
376 setMenuItemFlags();
377 return ok;
378}
379
380bool ImageViewer::reconvertImage()
381{
382 bool success = FALSE;
383
384 if ( image.isNull() ) return FALSE;
385
386 if ( alloc_context ) {
387 QColor::destroyAllocContext( alloc_context );
388 alloc_context = 0;
389 }
390 if ( useColorContext() ) {
391 alloc_context = QColor::enterAllocContext();
392 // Clear the image to hide flickering palette
393 QPainter painter(this);
394 painter.eraseRect(0, menubar->heightForWidth( width() ), width(), height());
395 }
396
397 QApplication::setOverrideCursor( waitCursor ); // this might take time
398 if ( pm.convertFromImage(image, conversion_flags) )
399 {
400 pmScaled = QPixmap();
401 scale();
402 resize( width(), height() );
403 success = TRUE; // load successful
404 } else {
405 pm.resize(0,0); // couldn't load image
406 }
407 updateStatus();
408 setMenuItemFlags();
409 QApplication::restoreOverrideCursor(); // restore original cursor
410
411 if ( useColorContext() )
412 QColor::leaveAllocContext();
413
414 return success; // TRUE if loaded OK
415}
416
417bool ImageViewer::smooth() const
418{
419 return options->isItemChecked(ss);
420}
421
422bool ImageViewer::useColorContext() const
423{
424 return options->isItemChecked(cc);
425}
426
427/*
428 This functions scales the pixmap in the member variable "pm" to fit the
429 widget size and puts the resulting pixmap in the member variable "pmScaled".
430*/
431
432void ImageViewer::scale()
433{
434 int h = height() - menubar->heightForWidth( width() ) - status->height();
435
436 if ( image.isNull() ) return;
437
438 QApplication::setOverrideCursor( waitCursor ); // this might take time
439 if ( width() == pm.width() && h == pm.height() )
440 { // no need to scale if widget
441 pmScaled = pm; // size equals pixmap size
442 } else {
443 if (smooth()) {
444 pmScaled.convertFromImage(image.smoothScale(width(), h),
445 conversion_flags);
446 } else {
447 QWMatrix m; // transformation matrix
448 m.scale(((double)width())/pm.width(),// define scale factors
449 ((double)h)/pm.height());
450 pmScaled = pm.xForm( m ); // create scaled pixmap
451 }
452 }
453 QApplication::restoreOverrideCursor(); // restore original cursor
454}
455
456/*
457 The resize event handler, if a valid pixmap was loaded it will call
458 scale() to fit the pixmap to the new widget size.
459*/
460
461void ImageViewer::resizeEvent( QResizeEvent * )
462{
463 status->setGeometry(0, height() - status->height(),
464 width(), status->height());
465
466 if ( pm.size() == QSize( 0, 0 ) ) // we couldn't load the image
467 return;
468
469 int h = height() - menubar->heightForWidth( width() ) - status->height();
470 if ( width() != pmScaled.width() || h != pmScaled.height())
471 { // if new size,
472 scale(); // scale pmScaled to window
473 updateStatus();
474 }
475 if ( image.hasAlphaBuffer() )
476 erase();
477}
478
479bool ImageViewer::convertEvent( QMouseEvent* e, int& x, int& y)
480{
481 if ( pm.size() != QSize( 0, 0 ) ) {
482 int h = height() - menubar->heightForWidth( width() ) - status->height();
483 int nx = e->x() * image.width() / width();
484 int ny = (e->y()-menubar->heightForWidth( width() )) * image.height() / h;
485 if (nx != x || ny != y ) {
486 x = nx;
487 y = ny;
488 updateStatus();
489 return TRUE;
490 }
491 }
492 return FALSE;
493}
494
495void ImageViewer::mousePressEvent( QMouseEvent *e )
496{
497 may_be_other = convertEvent(e, clickx, clicky);
498}
499
500void ImageViewer::mouseReleaseEvent( QMouseEvent * )
501{
502 if ( may_be_other )
503 other = this;
504}
505
506/*
507 Record the pixel position of interest.
508*/
509void ImageViewer::mouseMoveEvent( QMouseEvent *e )
510{
511 if (convertEvent(e,pickx,picky)) {
512 updateStatus();
513 if ((e->state()&LeftButton)) {
514 may_be_other = FALSE;
515 if ( clickx >= 0 && other) {
516 copyFrom(other);
517 }
518 }
519 }
520}
521
522/*
523 Draws the portion of the scaled pixmap that needs to be updated or prints
524 an error message if no legal pixmap has been loaded.
525*/
526
527void ImageViewer::paintEvent( QPaintEvent *e )
528{
529 if ( pm.size() != QSize( 0, 0 ) ) { // is an image loaded?
530 QPainter painter(this);
531 painter.setClipRect(e->rect());
532 painter.drawPixmap(0, menubar->heightForWidth( width() ), pmScaled);
533 }
534}
535
536
537/*
538 Explain anything that might be confusing.
539*/
540void ImageViewer::giveHelp()
541{
542 if (!helpmsg) {
543 QString helptext =
544 "<b>Usage:</b> <tt>showimg [-m] <i>filename ...</i></tt>"
545 "<blockquote>"
546 "<tt>-m</tt> - use <i>ManyColor</i> color spec"
547 "</blockquote>"
548 "<p>Supported input formats:"
549 "<blockquote>";
550 helptext += QImage::inputFormatList().join(", ");
551 helptext += "</blockquote>";
552
553 helpmsg = new QMessageBox( "Help", helptext,
554 QMessageBox::Information, QMessageBox::Ok, 0, 0, 0, 0, FALSE );
555 }
556 helpmsg->show();
557 helpmsg->raise();
558}
559
560void ImageViewer::copyFrom(ImageViewer* s)
561{
562 if ( clickx >= 0 ) {
563 int dx = clickx;
564 int dy = clicky;
565 int sx = s->clickx;
566 int sy = s->clicky;
567 int sw = QABS(clickx - pickx)+1;
568 int sh = QABS(clicky - picky)+1;
569 if ( clickx > pickx ) {
570 dx = pickx;
571 sx -= sw-1;
572 }
573 if ( clicky > picky ) {
574 dy = picky;
575 sy -= sh-1;
576 }
577 bitBlt( &image, dx, dy, &s->image, sx, sy, sw, sh );
578 reconvertImage();
579 repaint( image.hasAlphaBuffer() );
580 }
581}
582ImageViewer* ImageViewer::other = 0;
583
584void ImageViewer::hFlip()
585{
586 setImage(image.mirror(TRUE,FALSE));
587}
588
589void ImageViewer::vFlip()
590{
591 setImage(image.mirror(FALSE,TRUE));
592}
593
594void ImageViewer::rot180()
595{
596 setImage(image.mirror(TRUE,TRUE));
597}
598
599void ImageViewer::copy()
600{
601#ifndef QT_NO_MIMECLIPBOARD
602 QApplication::clipboard()->setImage(image); // Less information loss
603#endif
604}
605
606void ImageViewer::paste()
607{
608#ifndef QT_NO_MIMECLIPBOARD
609 QImage p = QApplication::clipboard()->image();
610 if ( !p.isNull() ) {
611 filename = "pasted";
612 setImage(p);
613 }
614#endif
615}
616
617void ImageViewer::setImage(const QImage& newimage)
618{
619 image = newimage;
620
621 pickx = -1;
622 clickx = -1;
623 setCaption( filename ); // set window caption
624 int w = image.width();
625 int h = image.height();
626 if ( !w )
627 return;
628
629 const int reasonable_width = 128;
630 if ( w < reasonable_width ) {
631 // Integer scale up to something reasonable
632 int multiply = ( reasonable_width + w - 1 ) / w;
633 w *= multiply;
634 h *= multiply;
635 }
636
637 h += menubar->heightForWidth(w) + status->height();
638 resize( w, h ); // we resize to fit image
639
640 reconvertImage();
641 repaint( image.hasAlphaBuffer() );
642
643 updateStatus();
644 setMenuItemFlags();
645}
646
647void ImageViewer::editText()
648{
649 ImageTextEditor editor(image,this);
650 editor.exec();
651}
652
653void ImageViewer::to1Bit()
654{
655 toBitDepth(1);
656}
657
658void ImageViewer::to8Bit()
659{
660 toBitDepth(8);
661}
662
663void ImageViewer::to32Bit()
664{
665 toBitDepth(32);
666}
667
668void ImageViewer::toBitDepth(int d)
669{
670 image = image.convertDepth(d);
671 reconvertImage();
672 repaint( image.hasAlphaBuffer() );
673}
Note: See TracBrowser for help on using the repository browser.