void AnnotationInterface::DynamicMouseMove( View& v, const DPoint& p, unsigned buttons, unsigned modifiers ) { // mouse move is processed only on active view if (view == 0 || v != *view) return; // get view image window ImageWindow w = view->Window(); // and image coordinates of the mouse position int imageX = RoundI(p.x); int imageY = RoundI(p.y); // set cursor to dragging cursor if mouse is inside one of the grip rectangles if (annotationPlaced && textRect.Includes(imageX, imageY)) w.SetDynamicCursor(move_all_XPM, 10, 10); else if (leaderPlaced && instance.annotationShowLeader && leaderRect.Includes(imageX, imageY)) w.SetDynamicCursor(move_all_XPM, 10, 10); else // otherwise, reset cursor w.ResetDynamicCursor(); // if we are currently dragging something if ( dragging != DraggingType::None ) { // compute delta int deltaX = imageX-lastX; int deltaY = imageY-lastY; // update text position if needed if (dragging == DraggingType::Text || dragging == DraggingType::Both) { instance.annotationPositionX += deltaX; instance.annotationPositionY += deltaY; } // update leader endpoint position if needed if (dragging == DraggingType::Leader || dragging == DraggingType::Both) { instance.annotationLeaderPositionX += deltaX; instance.annotationLeaderPositionY += deltaY; } // update annotation rectangle UpdateAnnotationRect(); // redraw dynamic view UpdateView(); // remember current point lastX = imageX; lastY = imageY; } }
void NumericControl::UpdateControls() { NumericEdit::UpdateControls(); int i0, i1; slider.GetRange( i0, i1 ); slider.SetValue( i0 + RoundI( (value - lowerBound)/(upperBound - lowerBound)*(i1 - i0) ) ); }
void AnnotationInterface::DynamicPaint( const View& v, VectorGraphics& g, const DRect& r ) const { // we need valid view and annotation must be placed if ( view == 0 || v != *view || !annotationPlaced ) return; // are we painting to rect with annotation? if ( !totalRect.Intersects( r ) ) return; // get the image window ImageWindow w = view->Window(); // render annotation to bitmap if needed if ( annotationBmp.IsNull() ) { relPosX = 0; relPosY = 0; annotationBmp = AnnotationRenderer::CreateAnnotationBitmap( instance, relPosX, relPosY, true ); screenBmp = Bitmap::Null(); } // compute viewport coordinates of annotation text int posX = instance.annotationPositionX - relPosX; int posY = instance.annotationPositionY - relPosY; w.ImageToViewport( posX, posY ); // compute zoom factor int zoomFactor = w.ZoomFactor(); double z = (zoomFactor < 0) ? -1.0/zoomFactor : double( zoomFactor ); // draw bitmap, scaled according to zoom factor Rect zr( RoundI( annotationBmp.Width()*z ), RoundI( annotationBmp.Height()*z ) ); if ( screenBmp.IsNull() || zr != screenBmp.Bounds() ) if ( zoomFactor < 0 ) screenBmp = annotationBmp.Scaled( z ); else screenBmp = annotationBmp; if ( zoomFactor < 0 ) g.DrawBitmap( posX, posY, screenBmp ); else g.DrawScaledBitmap( zr.MovedTo( posX, posY ), screenBmp ); }
static void EvaluateNoise( FVector& noiseEstimates, FVector& noiseFractions, StringList& noiseAlgorithms, const Image& image, int algorithm ) { SpinStatus spin; image.SetStatusCallback( &spin ); image.Status().Initialize( "Noise evaluation" ); image.Status().DisableInitialization(); Console console; int numberOfThreads = Thread::NumberOfThreads( image.NumberOfPixels(), 1 ); if ( numberOfThreads >= 3 ) { int numberOfSubthreads = RoundI( numberOfThreads/3.0 ); ReferenceArray<NoiseEvaluationThread> threads; threads.Add( new NoiseEvaluationThread( image, 0, algorithm, numberOfSubthreads ) ); threads.Add( new NoiseEvaluationThread( image, 1, algorithm, numberOfSubthreads ) ); threads.Add( new NoiseEvaluationThread( image, 2, algorithm, numberOfThreads - 2*numberOfSubthreads ) ); AbstractImage::ThreadData data( image, 0 ); // unbounded AbstractImage::RunThreads( threads, data ); for ( int i = 0; i < 3; ++i ) { noiseEstimates[i] = threads[i].noiseEstimate; noiseFractions[i] = threads[i].noiseFraction; noiseAlgorithms[i] = String( threads[i].noiseAlgorithm ); } threads.Destroy(); } else { for ( int i = 0; i < 3; ++i ) { NoiseEvaluationThread thread( image, i, algorithm, 1 ); thread.Run(); noiseEstimates[i] = thread.noiseEstimate; noiseFractions[i] = thread.noiseFraction; noiseAlgorithms[i] = String( thread.noiseAlgorithm ); } } image.ResetSelections(); image.Status().Complete(); console.WriteLn( "<end><cbr>Gaussian noise estimates:" ); for ( int i = 0; i < 3; ++i ) console.WriteLn( String().Format( "s%d = %.3e, n%d = %.4f ", i, noiseEstimates[i], i, noiseFractions[i] ) + '(' + noiseAlgorithms[i] + ')' ); }
void NewImageInterface::__ColorSample_Paint( Control& sender, const Rect& /*updateRect*/ ) { Graphics g( sender ); RGBA rgba = RGBAColor( float( instance.v0 ), float( instance.v1 ), float( instance.v2 ) ); if ( HASALPHA ) { g.SetBrush( Bitmap( ":/image-window/transparent-small.png" ) ); g.SetPen( Pen::Null() ); g.DrawRect( sender.BoundsRect() ); SetAlpha( rgba, RoundI( 255*instance.va ) ); } g.SetBrush( rgba ); g.SetPen( RGBAColor( 0, 0, 0 ) ); g.DrawRect( sender.BoundsRect() ); }
void AdaptiveStretchCurveGraphInterface::GenerateGraphCurve() { if ( m_curveBitmap.IsNull() ) m_curveBitmap = Bitmap( m_width, m_height ); m_curveBitmap.Fill( 0 ); // transparent if ( !m_curve.IsEmpty() ) { Array<Point> points( m_curveRect.Width() ); for ( int i = 0; i < m_curveRect.Width(); ++i ) { points[i].x = m_curveRect.x0 + i; points[i].y = RoundI( m_curveRect.y1 - 1 - (m_curveRect.Height() - 1)*m_curve( double( i )/(m_curveRect.Width() - 1) ) ); } Graphics G( m_curveBitmap ); G.EnableAntialiasing(); G.SetPen( m_curveColor ); G.DrawPolyline( points ); G.EndPaint(); } }
void Slider::SetNormalizedValue( double f ) { int v0, v1; GetRange( v0, v1 ); SetValue( v0 + RoundI( Range( f, 0.0, 1.0 )*(v1 - v0) ) ); }
void AnnotationInterface::DynamicMousePress( View& v, const DPoint& p, int button, unsigned buttons, unsigned modifiers ) { // we only use left mouse button if ( button != MouseButton::Left ) return; if (view == 0) { // can not run on previews if ( !v.IsMainView() ) throw Error( "Annotation cannot run on previews. Please select a main view." ); view = new View( v ); } // only handle events in our active view if (v != *view) return; // get view image window ImageWindow w = view->Window(); // and image coordinates of the click point int imageX = RoundI(p.x); int imageY = RoundI(p.y); // if annotation is not yet placed, place it now if (!annotationPlaced) { // set annotation position instance.annotationPositionX = imageX; instance.annotationPositionY = imageY; annotationPlaced = true; // place leader if needed if (instance.annotationShowLeader) { PlaceLeaderDefault(); } // update annotation rectangle UpdateAnnotationRect( true ); // redraw dynamic view UpdateView(); // start dragging mode until user release the mouse dragging = DraggingType::Text; w.SetDynamicCursor(move_all_XPM, 10, 10); } // if the annotation is already placed else { // if mouse is pressed on annotation text rectangle // let's start dragging if (textRect.Includes(imageX, imageY)) { // with Ctrl, both text and leader are dragged simulaneously if (modifiers & KeyModifier::Control) { dragging = DraggingType::Both; } // otherwise, just text is dragged else { dragging = DraggingType::Text; } } // if mouse is pressed on annotation leader rectangle and leader is visible // let's start dragging else if (instance.annotationShowLeader && leaderRect.Includes(imageX, imageY)) { // with Ctrl, both text and leader are dragged simulaneously if (modifiers & KeyModifier::Control) { dragging = DraggingType::Both; } // otherwise, just leader endpoint is dragged else { dragging = DraggingType::Leader; } } } // if any dragging is started, remember current point if (dragging != DraggingType::None) { lastX = imageX; lastY = imageY; } }
void NewImageInterface::UpdateInitialValue( Edit& e, Slider& s, double v ) { e.SetText( String().Format( "%.8lf", v ) ); s.SetValue( RoundI( v*s.MaxValue() ) ); }
void AdaptiveStretchCurveGraphInterface::GenerateGraphGrid() { pcl::Font font( m_fontFace ); font.SetPixelSize( m_fontSize ); int labelHeight = font.TightBoundingRect( "123" ).Height(); int labelSeparation = font.Width( '-' ); int xLabelHeight = labelHeight + labelSeparation; int yLabelWidth = font.Width( "1.0" ) + labelSeparation; m_curveRect = Rect( m_margin + yLabelWidth + m_tickSize, m_margin + m_tickSize, m_width - m_margin - m_tickSize, m_height - m_margin - xLabelHeight - m_tickSize ); if ( m_gridBitmap.IsNull() ) m_gridBitmap = Bitmap( m_width, m_height ); m_gridBitmap.Fill( m_backgroundColor ); Graphics G( m_gridBitmap ); G.SetPen( m_axisColor ); G.StrokeRect( m_curveRect ); G.SetFont( font ); // Plot horizontal axes + vertical grid for ( int w = 0; w <= 100; w += 10 ) { double f = w/100.0; int x = RoundI( m_curveRect.x0 + f*(m_curveRect.Width() - 1) ); G.DrawLine( x, m_curveRect.y0-m_tickSize, x, m_curveRect.y0 ); G.DrawLine( x, m_curveRect.y1, x, m_curveRect.y1+m_tickSize ); G.DrawText( x - labelHeight/2, m_height-m_margin, String().Format( "%.1f", f ) ); if ( w > 0 && w < 100 ) { G.SetPen( m_gridColor ); G.DrawLine( x, m_curveRect.y0+1, x, m_curveRect.y1-2 ); G.SetPen( m_axisColor ); } } // Plot vertical axes + horizontal grid for ( int h = 0; h <= 100; h += 10 ) { double f = h/100.0; int y = RoundI( m_curveRect.y1 - 1 - f*(m_curveRect.Height() - 1) ); G.DrawLine( m_curveRect.x0-m_tickSize, y, m_curveRect.x0, y ); G.DrawLine( m_curveRect.x1, y, m_curveRect.x1+m_tickSize, y ); G.DrawText( m_curveRect.x0-m_tickSize-yLabelWidth, y+labelHeight/2, String().Format( "%.1f", f ) ); if ( h > 0 && h < 100 ) { G.SetPen( m_gridColor ); G.DrawLine( m_curveRect.x0+1, y, m_curveRect.x1-2, y ); G.SetPen( m_axisColor ); } } G.EndPaint(); }
void JPCInstance::CreateImage( const ImageInfo& info ) { CheckOpenStream( !m_path.IsEmpty(), "CreateImage" ); InitJasPer(); if ( !info.IsValid() ) JP2KERROR( "Invalid image parameters in JPEG2000 file creation" ); if ( info.colorSpace != ColorSpace::RGB && info.colorSpace != ColorSpace::Gray ) JP2KERROR( "Unsupported color space in JPEG2000 file creation" ); if ( m_options.bitsPerSample != 8 ) if ( m_jp2Options.lossyCompression || m_options.bitsPerSample < 8 ) m_options.bitsPerSample = 8; else if ( m_options.bitsPerSample != 16 ) m_options.bitsPerSample = 16; if ( m_jp2CMProfile == nullptr ) m_options.embedICCProfile = false; m_options.ieeefpSampleFormat = m_options.complexSample = false; IsoString path8 = #ifdef __PCL_WINDOWS File::UnixPathToWindows( m_path ).ToMBS(); #else m_path.ToUTF8(); #endif m_jp2Stream = jas_stream_fopen( path8.c_str(), "wb" ); if ( m_jp2Stream == nullptr ) JP2KERROR( "Unable to create JPEG2000 file" ); m_jp2Image = jas_image_create0(); if ( m_jp2Image == nullptr ) JP2KERROR( "Unable to create JPEG2000 image" ); for ( int c = 0; c < info.numberOfChannels; ++c ) { jas_image_cmptparm_t p; ::memset( &p, 0, sizeof( jas_image_cmptparm_t ) ); p.tlx = p.tly = 0; // top-left corner position p.hstep = p.vstep = 1; // coordinate grid step sizes p.width = info.width; // width in pixels p.height = info.height; // height in pixels p.prec = m_options.bitsPerSample; // bit depth: 8 or 16 bits p.sgnd = m_jp2Options.signedSample; // signed or unsigned samples if ( jas_image_addcmpt( m_jp2Image, c, &p ) != 0 ) JP2KERROR( "Unable to create JPEG2000 image component" ); } if ( info.colorSpace == ColorSpace::Gray ) { jas_image_setclrspc( m_jp2Image, m_options.embedICCProfile ? JAS_CLRSPC_GENGRAY : JAS_CLRSPC_SGRAY ); jas_image_setcmpttype( m_jp2Image, 0, JAS_IMAGE_CT_COLOR( JAS_CLRSPC_CHANIND_GRAY_Y ) ); if ( info.numberOfChannels > 1 ) jas_image_setcmpttype( m_jp2Image, 1, JAS_IMAGE_CT_COLOR( JAS_IMAGE_CT_OPACITY ) ); } else { jas_image_setclrspc( m_jp2Image, m_options.embedICCProfile ? JAS_CLRSPC_GENRGB : JAS_CLRSPC_SRGB ); jas_image_setcmpttype( m_jp2Image, 0, JAS_IMAGE_CT_COLOR( JAS_CLRSPC_CHANIND_RGB_R ) ); jas_image_setcmpttype( m_jp2Image, 1, JAS_IMAGE_CT_COLOR( JAS_CLRSPC_CHANIND_RGB_G ) ); jas_image_setcmpttype( m_jp2Image, 2, JAS_IMAGE_CT_COLOR( JAS_CLRSPC_CHANIND_RGB_B ) ); if ( info.numberOfChannels > 3 ) jas_image_setcmpttype( m_jp2Image, 3, JAS_IMAGE_CT_COLOR( JAS_IMAGE_CT_OPACITY ) ); } if ( m_options.embedICCProfile ) jas_image_setcmprof( m_jp2Image, m_jp2CMProfile ); if ( m_jp2Options.resolutionData ) { m_jp2Image->rescm_ = m_options.metricResolution; m_jp2Image->hdispres_ = RoundI( m_options.xResolution ); m_jp2Image->vdispres_ = RoundI( m_options.yResolution ); } }
template <class P> inline static void WriteJPEGImage( const GenericImage<P>& img, JPEGWriter& writer, const ICCProfile& icc, JPEGFileData* fileData ) { if ( !writer.IsOpen() ) throw JPEG::WriterNotInitialized( String() ); if ( writer.Options().lowerRange >= writer.Options().upperRange ) throw JPEG::InvalidNormalizationRange( writer.Path() ); if ( img.IsEmptySelection() ) throw JPEG::InvalidPixelSelection( writer.Path() ); // Retrieve information on selected subimage. Rect r = img.SelectedRectangle(); int width = r.Width(); int height = r.Height(); int channels = img.NumberOfSelectedChannels(); // JPEG doesn't support alpha channels, just plain grayscale and RGB images. if ( channels != 1 && channels != 3 ) throw JPEG::InvalidChannelSelection( writer.Path() ); unsigned char qualityMarker[] = { 'J', 'P', 'E', 'G', 'Q', 'u', 'a', 'l', 'i', 't', 'y', 0, 0 }; try { // Make safe copies of critical parameters. fileData->lowerRange = writer.Options().lowerRange; fileData->upperRange = writer.Options().upperRange; // Initialize status callback. if ( img.Status().IsInitializationEnabled() ) img.Status().Initialize( String().Format( "Compressing JPEG: %d channel(s), %dx%d pixels", channels, width, height ), img.NumberOfSelectedSamples() ); // // Allocate and initialize a JPEG compressor instance. // // This struct contains the JPEG compression parameters and pointers to // working space, which is allocated as needed by the IJG library. fileData->compressor = new ::jpeg_compress_struct; // Set up normal JPEG error routines, passing a pointer to our custom // error handler structure. jpeg_compressor->err = ::jpeg_std_error( (::jpeg_error_mgr*)(fileData->errorManager = new JPEGErrorManager( writer.Path() )) ); // Override error_exit. JPEG library errors will be handled by throwing // exceptions from the JPEGErrorExit() routine. ((JPEGErrorManager*)fileData->errorManager)->error.error_exit = JPEGErrorExit; // Initialize the JPEG compression object. ::jpeg_create_compress( jpeg_compressor ); // // Set parameters for compression. // jpeg_compressor->image_width = width; jpeg_compressor->image_height = height; jpeg_compressor->input_components = channels; jpeg_compressor->in_color_space = (channels == 1) ? JCS_GRAYSCALE : JCS_RGB; // Set default compression parameters. ::jpeg_set_defaults( jpeg_compressor ); // Set any non-default parameters. if ( writer.JPEGOptions().quality == JPEGQuality::Unknown ) const_cast<JPEGImageOptions&>( writer.JPEGOptions() ).quality = JPEGQuality::Default; // Set up compression according to specified JPEG quality. // We limit to baseline-JPEG values (TRUE arg in jpeg_set_quality). ::jpeg_set_quality( jpeg_compressor, writer.JPEGOptions().quality, TRUE ); jpeg_compressor->optimize_coding = boolean( writer.JPEGOptions().optimizedCoding ); jpeg_compressor->arith_code = boolean( writer.JPEGOptions().arithmeticCoding ); jpeg_compressor->dct_method = JDCT_FLOAT; jpeg_compressor->write_JFIF_header = boolean( writer.JPEGOptions().JFIFMarker ); jpeg_compressor->JFIF_major_version = writer.JPEGOptions().JFIFMajorVersion; jpeg_compressor->JFIF_minor_version = writer.JPEGOptions().JFIFMinorVersion; jpeg_compressor->density_unit = writer.Options().metricResolution ? 2 : 1; jpeg_compressor->X_density = RoundI( writer.Options().xResolution ); jpeg_compressor->Y_density = RoundI( writer.Options().yResolution ); if ( writer.JPEGOptions().progressive ) ::jpeg_simple_progression( jpeg_compressor ); IsoString path8 = #ifdef __PCL_WINDOWS File::UnixPathToWindows( writer.Path() ).ToMBS(); #else writer.Path().ToUTF8(); #endif fileData->handle = ::fopen( path8.c_str(), "wb" ); if ( fileData->handle == 0 ) throw JPEG::UnableToCreateFile( writer.Path() ); // Specify data destination (e.g., a file). ::jpeg_stdio_dest( jpeg_compressor, fileData->handle ); // Start compressor. ::jpeg_start_compress( jpeg_compressor, TRUE ); // Output ICC Profile data. // We use APP2 markers, as stated on ICC.1:2001-12. if ( writer.Options().embedICCProfile && icc.IsProfile() ) { // Get ICC profile size in bytes from the ICC profile header uint32 byteCount = uint32( icc.ProfileSize() ); if ( byteCount != 0 ) { // The ICC profile must be writen as a sequence of chunks if its // size is larger than (roughly) 64K. // Number of whole chunks. int chunkCount = byteCount/0xFF00; // Remainder chunk size in bytes. int lastChunk = byteCount - chunkCount*0xFF00; // chunkCount includes a possible remainder from now on. if ( lastChunk != 0 ) ++chunkCount; // Allocate a working buffer. 64K bytes to hold chunk data, ICC // marker id, etc. ByteArray iccData( 0xFFFF ); // ICC JFIF marker identifier, as recommended by ICC.1:2001-12. // Note that this is a null-terminated string, thus the total // length is 12 bytes. ::memcpy( iccData.Begin(), "ICC_PROFILE", 12 ); // Byte #12, next to id's null terminator, is a 1-based chunk index. // Byte #13 is the total count of chunks (including remainder). iccData[13] = uint8( chunkCount ); // Write sequence of markers for ( int i = 1, offset = 0; i <= chunkCount; ++i ) { // Size in bytes of this chunk. int size = (i == chunkCount) ? lastChunk : 0xFF00; // Copy ICC profile data for this chunk, preserve ICC id, etc. ::memcpy( iccData.At( 14 ), icc.ProfileData().At( offset ), size ); // Keep track of this chunk's index number, one-based. iccData[12] = uint8( i ); // Output an APP2 marker for this chunk. ::jpeg_write_marker( jpeg_compressor, JPEG_APP0+2, (const JOCTET *)iccData.Begin(), size+14 ); // Prepare for next chunk. offset += size; } } } /* // Output IPTC PhotoInfo data. // We use an APP13 marker, as recommended by IPTC. if ( iptc != nullptr ) { size_type byteCount = iptc->MakeInfo( iptcPureData ); if ( byteCount != 0 ) { iptcData = new char[ byteCount+14 ]; ::strcpy( (char*)iptcData, "IPTCPhotoInfo" ); ::memcpy( ((char*)iptcData)+14, iptcPureData, byteCount ); delete (uint8*)iptcPureData, iptcPureData = nullptr; ::jpeg_write_marker( jpeg_compressor, JPEG_APP0+13, (const JOCTET *)iptcData, unsigned( byteCount+14 ) ); delete (uint8*)iptcData, iptcData = nullptr; } } */ // Output JPEGQuality marker. // We use an APP15 marker for quality. qualityMarker[12] = writer.JPEGOptions().quality; ::jpeg_write_marker( jpeg_compressor, JPEG_APP0+15, qualityMarker, 13 ); // // Write pixels row by row. // // JSAMPLEs per row in image_buffer. int row_stride = width * channels; // Make a one-row-high sample array. Array<JSAMPLE> buffer( row_stride ); // JPEG organization is chunky; PCL images are planar. Array<const typename P::sample*> v( channels ); // Either rescaling or truncating to the [0,1] range is mandatory when // writing floating-point images to JPEG files. bool rescale = P::IsFloatSample() && (fileData->upperRange != 1 || fileData->lowerRange != 0); double k = 1.0; if ( rescale ) k /= fileData->upperRange - fileData->lowerRange; while ( jpeg_compressor->next_scanline < jpeg_compressor->image_height ) { JSAMPLE* b = buffer.Begin(); for ( int c = 0; c < channels; ++c ) v[c] = img.PixelAddress( r.x0, r.y0+jpeg_compressor->next_scanline, img.FirstSelectedChannel()+c ); for ( int i = 0; i < width; ++i ) for ( int c = 0; c < channels; ++c, ++b ) { typename P::sample f = *v[c]++; if ( P::IsFloatSample() ) { f = typename P::sample( Range( double( f ), fileData->lowerRange, fileData->upperRange ) ); if ( rescale ) f = typename P::sample( k*(f - fileData->lowerRange) ); } P::FromSample( *b, f ); } b = buffer.Begin(); ::jpeg_write_scanlines( jpeg_compressor, &b, 1 ); img.Status() += width * channels; ++img.Status(); } // Finish JPEG compression. ::jpeg_finish_compress( jpeg_compressor ); // Close the output file. ::fclose( fileData->handle ), fileData->handle = nullptr; // Release the JPEG compression object. ::jpeg_destroy_compress( jpeg_compressor ); delete jpeg_compressor, fileData->compressor = nullptr; } catch ( ... ) { if ( fileData->handle != nullptr ) ::fclose( fileData->handle ), fileData->handle = nullptr; throw; } }