#include "MergeWidget.h" #include "CalamariWindow.h" #include <QFileDialog> #include <QImageReader> #include <QMessageBox> #include <QPainter> #include <QWhatsThis> #include "util.h" MergeWidget::MergeWidget( char* b, char* theirs, char* yours ) :BaseWidget( theirs, yours ) { base = QImage( b ); resultFormat = QImageReader::imageFormat( yours ); initMergeWidget(); spawnAlphaWindow(); } MergeWidget::MergeWidget( QImage b, QImage theirs, QImage yours ) :BaseWidget( theirs, yours ) { base = b; resultFormat = "PNG"; initMergeWidget(); } void MergeWidget::initMergeWidget() { allDone = false; alphaWindow = 0x0; if ( base.isNull() ) { //baseless merge base = QImage( leg1.size(), QImage::Format_ARGB32_Premultiplied ); base.fill( qRgb( 0, 0, 0 ) ); } if ( base.size() != leg1.size() ) { base = base.scaled( leg1.size() ); } merge = QImage( base.size(), QImage::Format_ARGB32_Premultiplied ); base_theirs_t = base_yours_t = conflicts_t = -1; base_theirs = QBitmap( base.size() ); base_yours = QBitmap( base.size() ); conflicts = QBitmap( base.size() ); main = editor = new MergeEditor; main_l->addWidget( main ); editor->setImage( &merge ); editor->setTheirs( &leg1 ); editor->setYours( &leg2 ); slider->setRange( 0, 255 ); slider->setValue( 127 ); slider->setSingleStep( 16 ); slider->setPageStep( 255 ); slider->setEnabled( false ); connect( slider, SIGNAL( valueChanged(int) ), editor, SLOT( setBlend(int) ) ); editor->setBlend( 127 ); connect( editor, SIGNAL( imageEdited() ), this, SLOT( imageEdited() ) ); p1->setPalette( QPalette( QColor( 0, 0, 100 ) ) ); p2->setPalette( QPalette( QColor( 0, 100, 0 ) ) ); p1->setVeilColor( QColor( 0, 0, 100 ) ); p2->setVeilColor( QColor( 0, 100, 0 ) ); p1->setFrameStyle( QFrame::Box ); p2->setFrameStyle( QFrame::Box ); } MergeWidget::~MergeWidget(void) { } void MergeWidget::autoMerge() { //Get all the diffs calculated. freshenConflicts(); QPainter p; p.begin( &merge ); p.setCompositionMode( QPainter::CompositionMode_Source ); //Start with the yours file. p.drawImage( 0, 0, leg2 ); //Overlay all of the theirs diffs. QRegion all( merge.rect() ); QRegion t( base_theirs ); t = all.subtract( t ); if ( !t.isEmpty() ) { p.setClipRegion( t ); p.drawImage( 0, 0, leg1 ); } //Overlay the conflicts. QRegion c( conflicts ); c = all.subtract( c ); if ( !c.isEmpty() ) { p.setClipRegion( c ); p.fillRect( merge.rect(), conflict ); } p.end(); editor->update(); } void MergeWidget::setTheirsMask( bool on ) { if ( base_theirs_t != tolerance ) { imageDiff( base, leg1, &base_theirs, tolerance ); base_theirs_t = tolerance; } main->setMask( &base_theirs ); main->setMaskEnabled( on ); } void MergeWidget::setYoursMask( bool on ) { if ( base_yours_t != tolerance ) { imageDiff( base, leg2, &base_yours, tolerance ); base_yours_t = tolerance; } main->setMask( &base_yours ); main->setMaskEnabled( on ); } void MergeWidget::setConflictMask( bool on ) { freshenConflicts(); main->setMask( &conflicts ); main->setMaskEnabled( on ); } void MergeWidget::setBrushSize( int px ) { editor->setBrushSize( px ); } void MergeWidget::setEraserSize( int px ) { editor->setEraserSize( px ); } void MergeWidget::setEraserColor( QColor e ) { BaseWidget::setEraserColor( e ); editor->setEraserColor( e ); } void MergeWidget::eraserToggle( bool on ) { if ( !on ) return; editor->setMode( MergeEditor::Erase ); slider->setEnabled( false ); } void MergeWidget::paintToggle( bool on ) { if ( !on ) return; editor->setMode( MergeEditor::Paint ); slider->setEnabled( true ); } void MergeWidget::sprayToggle( bool on ) { if ( !on ) return; editor->setMode( MergeEditor::Spray ); slider->setEnabled( true ); } bool MergeWidget::save() { if ( result.isNull() ) { result = QFileDialog::getSaveFileName ( this, "Save Result As", QString(), "*."+resultFormat ); } if ( result.isNull() ) return false; QImage final = merge; if ( alphaWindow ) final.setAlphaChannel( alphaWindow->getResult() ); if ( !final.save( result, resultFormat.toAscii() ) ) { QMessageBox::critical( this, "CALAMARI error", "Unable to save " + result ); return false; } emit enableSave( false ); return true; } void MergeWidget::saveAs() { QString as = QFileDialog::getSaveFileName ( this, "Save Result As", QString(), "*."+resultFormat ); if ( as.isNull() ) return; QImage final = merge; if ( alphaWindow ) final.setAlphaChannel( alphaWindow->getResult() ); if ( !final.save( as, resultFormat.toAscii() ) ) { QMessageBox::critical( this, "CALAMARI error", "Unable to save " + as ); } } void MergeWidget::setResult( char* resultFile ) { result = resultFile; } void MergeWidget::exportStencils() { freshenConflicts(); QString prefix = result; if ( prefix.isNull() ) { prefix = QFileDialog::getSaveFileName ( this, "Select Filename Prefix for Stencils", QString(), "*.*", 0, QFileDialog::DontConfirmOverwrite ); } if ( prefix.isNull() ) return; if ( prefix.contains( '.' ) ) prefix.truncate( prefix.lastIndexOf( '.' ) ); base_theirs.save( prefix + "_stencil_theirs.png", "PNG" ); base_yours.save( prefix + "_stencil_yours.png", "PNG" ); conflicts.save( prefix + "_stencil_conflicts.png", "PNG" ); QWhatsThis::showText( mapToGlobal(QPoint(0,0)), "Saved\n" + prefix + "_stencil_theirs.png\n" + prefix + "_stencil_yours.png\n" + prefix + "_stencil_conflicts.png\n", this ); } void MergeWidget::freshenConflicts() { //Do diffs vs. base. if ( base_theirs_t != tolerance ) { imageDiff( base, leg1, &base_theirs, tolerance ); } if ( base_yours_t != tolerance ) { imageDiff( base, leg2, &base_yours, tolerance ); } base_theirs_t = base_yours_t = tolerance; //Conflicts. if ( conflicts_t != tolerance ) { //Diffs between the legs. QBitmap theirs_yours( base.size() ); imageDiff( leg1, leg2, &theirs_yours, tolerance ); //The conflict stencil is the result of drawing //all three other stencils on top of each other. conflicts.fill( Qt::color0 ); QPainter p( &conflicts ); p.setPen( Qt::color1 ); p.drawPixmap( 0, 0, base_theirs ); p.drawPixmap( 0, 0, base_yours ); p.drawPixmap( 0, 0, theirs_yours ); conflicts_t = tolerance; } } void MergeWidget::spawnAlphaWindow() { QImage base_a = base.alphaChannel(); QImage theirs_a = leg1.alphaChannel(); QImage yours_a = leg2.alphaChannel(); bool opaque = true; for ( int i = 0 ; i < base_a.width() ; i++ ) { for ( int j = 0 ; j < base_a.height() ; j++ ) { if ( base_a.pixelIndex( i, j ) < 255 || theirs_a.pixelIndex( i, j ) < 255 || yours_a.pixelIndex( i, j ) < 255 ) { opaque = false; break; } } if ( !opaque ) break; } if ( opaque ) return; //no alpha channel to speak of //Wipe alpha channel of our images so that it doesn't interfere with merging. for ( int i = 0 ; i < base.width() ; i++ ) { for ( int j = 0 ; j < base.height() ; j++ ) { base.setPixel( i, j, qRgb( qRed( base.pixel( i, j ) ), qGreen( base.pixel( i, j ) ), qBlue( base.pixel( i, j ) ) ) ); leg1.setPixel( i, j, qRgb( qRed( leg1.pixel( i, j ) ), qGreen( leg1.pixel( i, j ) ), qBlue( leg1.pixel( i, j ) ) ) ); leg2.setPixel( i, j, qRgb( qRed( leg2.pixel( i, j ) ), qGreen( leg2.pixel( i, j ) ), qBlue( leg2.pixel( i, j ) ) ) ); } } alphaWindow = new CalamariWindow( base_a, theirs_a, yours_a, this ); alphaWindow->show(); } void MergeWidget::beDone() { allDone = true; if ( alphaWindow ) alphaWindow->close(); }
# | Change | User | Description | Committed | |
---|---|---|---|---|---|
#8 | 5549 | Sam Stafford |
Fix another edge case bug where Qt's behavior is the opposite of what I'd expect - when an empty clip region is set, the clip region is cleared entirely, as if a FULL clip region had been set. The resulting bug was that any image which had zero conflicts would show up as one gigantic conflict - ack! The code now checks for this case and bails out of any painting operation that's about to use an empty clip region. |
||
#7 | 5539 | Sam Stafford | Kick off another window to merge alpha channels, if present. | ||
#6 | 5534 | Sam Stafford |
A brand new help file, a bit of cosmetic sprucing, one critical bug fix, and a partridge in a pear tree. |
||
#5 | 5527 | Sam Stafford |
Diff options in merge mode, and an "export stencils" command for people who like that sort of thing. Also, a fresh build. |
||
#4 | 5524 | Sam Stafford | Eraser and spraypaint tools, and "Save" command. | ||
#3 | 5519 | Sam Stafford |
Added the paintbrush tool. Now we're getting somewhere. |
||
#2 | 5516 | Sam Stafford | The M is for Merge. | ||
#1 | 5511 | Sam Stafford | Continued infrastructure work. |