/*************************************************************************** qsimage.cpp ------------------- begin : 01-January-2000 copyright : (C) 2000 by Kamil Dobkowski email : kamildobk@poczta.onet.pl ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ #include "qsimage.h" #include #include struct QSImage::image_runtime_data { enum Type { RGBImage, GrayImage, IndexedImage } type; struct { QSPt2 p1; QSPt2 p2; } rarea; // area to refresh int pi; int pj; int lines; int curr_x_index; int curr_y_index; QSAxis *xaxis; QSAxis *yaxis; QSAxis *vaxis; QSMatrix *rm; QSMatrix *gm; QSMatrix *bm; QSMatrix *pm; int w, h, ph; int *xbuff; int *ybuff; bool is_x_vector; bool is_y_vector; QSGFill fill; QSDrv::PixmapBuffer buff; }; #define CHANNELS_NUM 6 //-------------------------------------------------------------// QSImage::QSImage(QSAxes* parent, const char * name) :QSPlot2D(parent,name) { assert( parent ); m_use_gradient = true; m_axes = parent; m_evalid = false; m_rawmode = false; m_title_str = tr("Untitled image"); m_dmin = 0.0; m_dmax = 0.0; initChannelTable( CHANNELS_NUM ); initAttributeTables( 0, 0, 0, 0 ); } //-------------------------------------------------------------// QSImage::~QSImage() { } //-------------------------------------------------------------// void QSImage::setUseGradient( bool enable ) { if ( m_use_gradient != enable ) { parametersChanging(); m_use_gradient = enable; parametersChanged(); } } //-------------------------------------------------------------// void QSImage::setRawMode( bool enabled ) { if ( m_rawmode != enabled ) { parametersChanging(); m_rawmode = enabled; m_evalid = false; parametersChanged(); } } //-------------------------------------------------------------// void QSImage::allocRuntimeData() { QSPlot2D::allocRuntimeData(); d = new image_runtime_data(); d->is_x_vector = false; d->is_y_vector = false; d->xbuff = NULL; d->ybuff = NULL; d->pi = 0; d->pj = 0; d->xaxis = defaultAxis(QSAxis::XAxisType); d->yaxis = defaultAxis(QSAxis::YAxisType); d->vaxis = defaultAxis(QSAxis::VAxisType); d->w = matrixCols( DataRed ); d->h = matrixRows( DataRed ); d->ph = matrixRows( Palette ); d->rm = matrix( DataRed ); d->gm = matrix( DataGreen ); d->bm = matrix( DataBlue ); d->pm = matrix( Palette ); // detect image type d->type = image_runtime_data::GrayImage; if ( matrixCols( Palette ) == 3 && matrixRows( Palette ) > 0 ) d->type = image_runtime_data::IndexedImage; if ( matrixCols( DataGreen ) == d->w && matrixRows( DataGreen ) == d->h && matrixCols( DataBlue ) == d->w && matrixRows( DataBlue ) == d->h ) d->type = image_runtime_data::RGBImage; if ( matrixCols( XVector ) == d->w+1 && matrixRows( XVector ) > 0 ) d->is_x_vector = true; else d->is_x_vector = false; if ( matrixRows( YVector ) == d->h+1 && matrixCols( YVector ) > 0 ) d->is_y_vector = true; else d->is_y_vector = false; if ( m_rawmode ) { m_dmin = 0.0; m_dmax = 255.0; m_evalid = true; } } //-------------------------------------------------------------// void QSImage::freeRuntimeData() { delete[] d->xbuff; d->xbuff = NULL; delete[] d->ybuff; d->ybuff = NULL; delete d; d = NULL; QSPlot2D::freeRuntimeData(); } //-------------------------------------------------------------// bool QSImage::getAxisRange( QSAxis *axis, double& min, double& max ) { allocRuntimeData(); if ( d->w == 0 || d->h == 0 ) { freeRuntimeData(); return false; } if ( !m_evalid ) { for( int j=0; jh; j++ ) { for( int i=0; iw; i++ ) { double vmax; double vmin; vmax = vmin = value(j,i,DataRed); if ( d->type == image_runtime_data::RGBImage ) { double vg = value( j, i, DataGreen ); double vb = value( j, i, DataBlue ); vmax = QMAX( vmax, QMAX( vg, vb ) ); vmin = QMIN( vmin, QMIN( vg, vb ) ); } if ( i == 0 && j == 0 ) { m_dmax = vmax; m_dmin = vmin; } else { m_dmin = QMIN( m_dmin, vmin ); m_dmax = QMAX( m_dmax, vmax ); } } // for ( i= .. } // for ( j= ... m_evalid = true; } double xmin = d->is_x_vector ? matrix(XVector)->value(0,0) : 0.0; double xmax = d->is_x_vector ? matrix(XVector)->value(0,d->w) : d->w; double ymin = d->is_y_vector ? matrix(YVector)->value(0,0) : 0.0; double ymax = d->is_y_vector ? matrix(YVector)->value(d->h,0) : d->h; if ( xmin > xmax ) { double xtemp = xmin; xmin = xmax; xmax = xtemp; } if ( ymin > ymax ) { double ytemp = ymin; ymin = ymax; ymax = ytemp; } if ( axis == d->xaxis ) { min = xmin; max = xmax; } else if ( axis == d->yaxis ) { min = ymin; max = ymax; } else if ( axis == d->vaxis && m_rawmode ) { min = 0.0; max = 255.0; } else if ( axis == d->vaxis && !m_rawmode ) { min = m_dmin; max = m_dmax; } else { freeRuntimeData(); return false; } freeRuntimeData(); return true; } //-------------------------------------------------------------// bool QSImage::start() { QSPlot2D::start(); if ( !init_buffers() ) return false; // Init loops d->pi = d->rarea.p1.x; d->pj = d->rarea.p1.y; d->curr_x_index = -999999; // see set_rgb d->curr_y_index = -999999; d->lines = 0; return true; } //-------------------------------------------------------------// bool QSImage::init_buffers() { // detect QSPt2f p1; QSPt2f p2; p1.x = d->is_x_vector ? matrix(XVector)->value(0,0) : 0.0; p1.y = d->is_y_vector ? matrix(YVector)->value(0,0) : 0.0; p2.x = d->is_x_vector ? matrix(XVector)->value(0,d->w) : d->w; p2.y = d->is_y_vector ? matrix(YVector)->value(d->h,0) : d->h; p1 = m_axes->dataToCanvas(p1,d->xaxis,d->yaxis); p2 = m_axes->dataToCanvas(p2,d->xaxis,d->yaxis); QSPt2 cp1( int(p1.x+0.5), int(p1.y+0.5) ); QSPt2 cp2( int(p2.x+0.5), int(p2.y+0.5) ); if ( cp1.x > cp2.x ) { int temp = cp1.x; cp1.x = cp2.x; cp2.x = temp; } if ( cp1.y > cp2.y ) { int temp = cp1.y; cp1.y = cp2.y; cp2.y = temp; } // Intersect refresh area with clipping area // clipping area is set in plot2d as an interior // of the axis box. if ( !clip_rect(cp1,cp2) ) return false; // where bitmap starts d->rarea.p1 = cp1; d->rarea.p2 = cp2; // X index buffer // index buffer tells, for each screen pixel between rarea.x1 and // rarea.x2, which data column is to be drawn at this position d->xbuff = new int[d->rarea.p2.x-d->rarea.p1.x+1]; for ( int i=0; iw; i++ ) { // for each data column double dx1 = d->is_x_vector ? matrix(XVector)->value(0,i) : i; double dx2 = d->is_x_vector ? matrix(XVector)->value(0,i+1) : i+1; int cx1 = int(m_axes->dataToCanvas( QSPt2f(dx1,0.0), d->xaxis, d->yaxis ).x+0.5); //start pos of this column on the screen int cx2 = int(m_axes->dataToCanvas( QSPt2f(dx2,0.0), d->xaxis, d->yaxis ).x+0.5); // end pos of this column on the screen if ( cx2 < cx1 ) { int temp = cx1; cx1 = cx2; cx2 = temp; } for ( int j=cx1; j<=cx2; j++ ) // for ach screen pixel beetween cx2 an cx1 if ( j>=d->rarea.p1.x && j<=d->rarea.p2.x ) d->xbuff[j-d->rarea.p1.x] = i; } // Y index buffer d->ybuff = new int[d->rarea.p2.y-d->rarea.p1.y+1]; for ( int i=0; ih; i++ ) { double dy1 = d->is_y_vector ? matrix(YVector)->value(i,0) : i; double dy2 = d->is_y_vector ? matrix(YVector)->value(i+1,0) : i+1; int cy1 = int(m_axes->dataToCanvas( QSPt2f(0.0,dy1), d->xaxis, d->yaxis ).y+0.5); int cy2 = int(m_axes->dataToCanvas( QSPt2f(0.0,dy2), d->xaxis, d->yaxis ).y+0.5); if ( cy2 < cy1 ) { int temp = cy1; cy1 = cy2; cy2 = temp; } for ( int j=cy1; j<=cy2; j++ ) if ( j>=d->rarea.p1.y && j<=d->rarea.p2.y ) d->ybuff[j-d->rarea.p1.y] = i; } return true; } //-------------------------------------------------------------// bool QSImage::clip_rect( QSPt2& p1, QSPt2& p2 ) { int cx1; int cy1; int cx2; int cy2; //dynamic_cast(m_axes->proj())->getClipRect( &cx1, &cy1, &cx2, &cy2 ); QSPt2f pos1 = m_axes->proj()->world2DToCanvas( QSPt2f(0.0,0.0) ); QSPt2f pos2 = m_axes->proj()->world2DToCanvas( QSPt2f(1.0,1.0) ); cx1 = QMIN( int(pos1.x+0.5), int(pos2.x+0.5) ); cy1 = QMIN( int(pos1.y+0.5), int(pos2.y+0.5) ); cx2 = QMAX( int(pos1.x+0.5), int(pos2.x+0.5) ); cy2 = QMAX( int(pos1.y+0.5), int(pos2.y+0.5) ); int x01; int y01; int x02; int y02; if ( cx1 > p1.x ) x01 = cx1; else x01 = p1.x; if ( cy1 > p1.y ) y01 = cy1; else y01 = p1.y; if ( cx2 < p2.x ) x02 = cx2; else x02 = p2.x; if ( cy2 < p2.y ) y02 = cy2; else y02 = p2.y; x02 -= 1; y02 -= 1; if ( x02-x01 > 0 && y02-y01 > 0 ) { p1.x = x01; p1.y = y01; p2.x = x02; p2.y = y02; return true; } return false; } //-------------------------------------------------------------// void QSImage::end() { // all clean-up work is done in freeRuntimeData() QSPlot2D::end(); } //-------------------------------------------------------------// void QSImage::dataChanged( int channel ) // Ouu. We need to calculate new extremes in data // Refresh data on screen also. { m_evalid = false; m_dmin = m_dmax = 0.0; QSPlot2D::dataChanged( channel ); } //-------------------------------------------------------------// bool QSImage::step() { int curr_step = 0; while( d->pj <= d->rarea.p2.y ) { // Get pixmap buffer for the next buff.lines lines if ( d->lines == 0 ) { d->buff.ptr = NULL; m_drv->getPixmapBuffer( &d->buff, d->rarea.p2.x-d->rarea.p1.x+1, d->rarea.p2.y-d->pj+1 ); if ( d->buff.ptr == NULL || d->buff.lines == 0 ) { return false; } m_ptr = d->buff.ptr; d->lines = d->buff.lines; } int ypos = d->ybuff[d->pj-d->rarea.p1.y]; // Write scanlines to the buffer while( d->pi <= d->rarea.p2.x ) { set_rgb( m_ptr, d->xbuff[d->pi-d->rarea.p1.x], ypos ); d->pi ++; m_ptr += d->buff.po; if ( curr_step++ > (work_steps<<5) && m_bkg_handler ) return true; } d->pj ++; // Draw pixmap if ( --d->lines == 0 ) { QSPt2 ppos( d->rarea.p1.x, d->pj-d->buff.lines ); m_drv->drawPixmap( QSPt2f( ppos.x, ppos.y ), &d->buff ); } // Set pointer to the next scanline m_ptr = d->buff.ptr + (d->buff.lines-d->lines) * d->buff.lo; d->pi = d->rarea.p1.x; } return false; } //-------------------------------------------------------------// void QSImage::set_rgb( unsigned char* p, int x_index, int y_index ) { double dr = 0.0; double dg = 0.0; double db = 0.0; if ( d->curr_x_index != x_index || d->curr_y_index != y_index ) { dr = d->rm->value(y_index,x_index);//c if ( d->type == image_runtime_data::RGBImage ) { dg = d->gm->value(y_index,x_index);//c db = d->bm->value(y_index,x_index);//c } // convert from data to world if ( !m_rawmode ) { if ( !m_use_gradient ) { dr = d->vaxis->dataToWorld(dr)*255.0+0.5; if ( d->type == image_runtime_data::RGBImage ) { dg = d->vaxis->dataToWorld(dg)*255.0+0.5; db = d->vaxis->dataToWorld(db)*255.0+0.5; } } else { m_gradient.fill( d->vaxis->dataToWorld(dr), d->fill ); m_r = d->fill.color.r; m_g = d->fill.color.g; m_b = d->fill.color.b; } } // take a palette value if ( !m_use_gradient ) if ( d->type == image_runtime_data::IndexedImage ) { if ( dr < 0 ) dr = 0; if ( dr > d->ph-1 ) dr = d->ph-1; int pindex = int(dr); dr = d->pm->value( pindex,0 ); //c dg = d->pm->value( pindex,1 ); db = d->pm->value( pindex,2 ); } if ( !m_use_gradient ) { if ( dr < 0.0 ) dr = 0.0; if ( dr > 255.0 ) dr = 255.0; } // finally set m_r, m_g, m_b values if ( !m_use_gradient ) if ( d->type == image_runtime_data::GrayImage ) { m_r = m_g = m_b = (unsigned char )dr; } else { if ( dg < 0.0 ) dg = 0.0; if ( db < 0.0 ) db = 0.0; if ( dg > 255.0 ) dg = 255.0; if ( db > 255.0 ) db = 255.0; m_r = (unsigned char )dr; m_g = (unsigned char )dg; m_b = (unsigned char )db; } d->curr_x_index = x_index; d->curr_y_index = y_index; } *p = 255; p += d->buff.co;// alpha *p = m_r; p += d->buff.co; *p = m_g; p += d->buff.co; *p = m_b; } //-------------------------------------------------------------// QString QSImage::posInfo( QSPt2f& p ) { if ( m_busy ) return QString::null; int pindex; QString result; allocRuntimeData(); if ( !init_buffers() ) { freeRuntimeData(); return QString::null; } QSPt2 pos( int(p.x+0.5), int(p.y+0.5) ); if ( pos.x >= d->rarea.p1.x && pos.x <= d->rarea.p2.x && pos.y >= d->rarea.p1.y && pos.y <= d->rarea.p2.y ) { QSPt2 index( d->xbuff[pos.x-d->rarea.p1.x], d->ybuff[pos.y-d->rarea.p1.y] ); result = QString(tr(" row = ")) + QString::number(index.y) + "\n"; result += QString(tr(" col = ")) + QString::number(index.x) + "\n"; //QSPt2f p = m_proj->canvasToWorld2D( pos ); //p = worldToData( p ); //result += QString(tr(" X = ")) + QString::number(p.x) + "\n"; //result += QString(tr(" Y = ")) + QString::number(p.y) + "\n"; switch( d->type ) { case image_runtime_data::GrayImage: result += QString(tr(" value = ")) + QString::number(d->rm->value(index.y,index.x)) + "\n"; break; case image_runtime_data::RGBImage: result += QString(tr(" R = ")) + QString::number(d->rm->value(index.y,index.x)) + "\n"; result += QString(tr(" G = ")) + QString::number(d->gm->value(index.y,index.x)) + "\n"; result += QString(tr(" B = ")) + QString::number(d->bm->value(index.y,index.x)) + "\n"; break; case image_runtime_data::IndexedImage: pindex = (int )d->rm->value(index.y,index.x); pindex = QMAX( pindex, 0 ); pindex = QMIN( pindex, d->ph-1 ); result += QString(tr(" palette index = ")) + QString::number(pindex) + "\n"; result += QString(tr(" R = ")) + QString::number(d->pm->value(pindex,0)) + "\n"; result += QString(tr(" G = ")) + QString::number(d->pm->value(pindex,1)) + "\n"; result += QString(tr(" B = ")) + QString::number(d->pm->value(pindex,2)) + "\n"; /* result += QString(tr(" G = ")) + QString::number(d->pm->ncol()) + "\n"; result += QString(tr(" B = ")) + QString::number(d->pm->ncol()) + "\n"; */ default: break; } } freeRuntimeData(); return result; } //-------------------------------------------------------------// bool QSImage::isClicked( const QSPt2f& pos ) { if ( m_busy ) return false; allocRuntimeData(); if ( !init_buffers() ) { freeRuntimeData(); return false; } if ( pos.x >= d->rarea.p1.x && pos.x <= d->rarea.p2.x && pos.y >= d->rarea.p1.y && pos.y <= d->rarea.p2.y ) return true; freeRuntimeData(); return false; } //-------------------------------------------------------------// #define BOX_SPACE 2.0 #define BOX_WIDTH 20.0 #define BOX_HEIGHT 100.0 QSPt2f QSImage::legendItemSize( QSDrv *drv ) { double boxSpace = drv->toPixels(BOX_SPACE); QSPt2f boxSize( drv->toPixels(BOX_WIDTH), drv->toPixels(BOX_HEIGHT) ); QSPt2f tsize = drv->rTextSize( 270, title() ); QSPt2f lsize; int nr_labels = 0; QSAxis *axis = defaultAxis(QSAxis::VAxisType); const list *tics = axis->tics(); list::const_iterator curr = tics->begin(); list::const_iterator last = tics->end(); while( curr != last ) { if ( !(*curr).m_major ) { curr++; continue; } if ( !(*curr).m_label.isEmpty() ) { QSPt2f size = drv->rTextSize( (*curr).m_angle, (*curr).m_label ); lsize.x = QMAX(lsize.x, size.x); lsize.y = QMAX(lsize.y, size.y); } curr++; nr_labels++; } boxSize.y = QMAX(boxSize.y,nr_labels*lsize.y); return QSPt2f( tsize.x+boxSpace+boxSize.x+boxSpace+boxSpace+boxSpace+lsize.x, QMAX(tsize.y,boxSize.y) ); } //-------------------------------------------------------------// void QSImage::drawLegendItem( const QSPt2f& pos, QSDrv *drv ) { double boxSpace = drv->toPixels(BOX_SPACE); QSPt2f boxSize( drv->toPixels(BOX_WIDTH), drv->toPixels(BOX_HEIGHT) ); // detect image type and number of colors in gradient int h = 256; int type = image_runtime_data::GrayImage; if ( matrixCols( Palette ) == 3 && matrixRows( Palette ) > 0 ) { type = image_runtime_data::IndexedImage; h = matrixRows( Palette ); } if ( matrixCols( DataGreen ) == matrixCols( DataRed ) && matrixRows( DataGreen ) == matrixRows( DataRed ) && matrixCols( DataBlue ) == matrixCols( DataRed ) && matrixRows( DataBlue ) == matrixRows( DataRed ) ) type = image_runtime_data::RGBImage; // title QSPt2f tsize = drv->rTextSize( 270, title() ); double height = boxSize.y = legendItemSize(drv).y; drv->drawRText( QSPt2f(pos.x,pos.y+height/2), 270, title(), AlignHCenter | AlignTop ); // gradient QSAxis *axis = defaultAxis(QSAxis::VAxisType); QSPt2f cpos( pos.x+tsize.x+boxSpace, pos.y+(height-boxSize.y)/2.0 ); drv->setLine( QSGLine::invisibleLine ); for ( int i=0; isetFill( f ); drv->drawRect( p1, p2 ); break; case image_runtime_data::IndexedImage: /* f.color = QSGColor( (unsigned char )matrix(Palette)->value(index,0), (unsigned char )matrix(Palette)->ncol(), (unsigned char )matrix(Palette)->ncol() ); */ f.color = QSGColor( (unsigned char )matrix(Palette)->value(index,0), (unsigned char )matrix(Palette)->value(index,1), (unsigned char )matrix(Palette)->value(index,2) ); drv->setFill( f ); drv->drawRect( p1, p2 ); break; case image_runtime_data::RGBImage: f.color = QSGColor( index, 0, 0 ); drv->setFill( f ); drv->drawRect( QSPt2f(p1.x,p1.y), QSPt2f(x01,p2.y) ); f.color = QSGColor( 0, index, 0 ); drv->setFill( f ); drv->drawRect( QSPt2f(x01,p1.y), QSPt2f(x02,p2.y) ); f.color = QSGColor( 0, 0, index ); drv->setFill( f ); drv->drawRect( QSPt2f(x02,p1.y), QSPt2f(p2.x,p2.y) ); break; } } /* // frame drv->setFill( QSGFill::transparentFill ); drv->drawRect( cpos, cpos+boxSize ); */ QSGLine l; drv->setLine( l ); cpos.x = cpos.x+boxSize.x; // labels const list *tics = axis->tics(); list::const_iterator curr = tics->begin(); list::const_iterator last = tics->end(); while( curr != last ) { if ( !(*curr).m_major ) { curr++; continue; } double ypos = cpos.y+ (1.0-(*curr).m_pos)*boxSize.y+0.5; drv->drawLine( QSPt2f(cpos.x,ypos), QSPt2f(cpos.x+boxSpace,ypos) ); drv->drawRText( QSPt2f(cpos.x+boxSpace+boxSpace+boxSpace,ypos), (*curr).m_angle, (*curr).m_label, AlignLeft | AlignVCenter ); curr++; } } //-------------------------------------------------------------// void QSImage::loadStateFromStream( QDataStream& stream, QSObjectFactory *factory ) { QSPlot2D::loadStateFromStream( stream, factory ); } //-------------------------------------------------------------// void QSImage::saveStateToStream( QDataStream& stream, QSObjectFactory *factory ) { QSPlot2D::saveStateToStream( stream, factory ); } //-------------------------------------------------------------// QString QSImage::channelVariable( int channel ) const { switch( channel ) { case DataRed: return "r"; case DataGreen: return "g"; case DataBlue: return "b"; case Palette: return "p"; case XVector: return "x"; case YVector: return "y"; } return QString::null; } //-------------------------------------------------------------// QSImage::ColumnType QSImage::columnType( int channel, int column ) const { }