diff --git a/src/Image/Image_PixMap.cdl b/src/Image/Image_PixMap.cdl index 8bf6ffcfaf..ad528f3721 100644 --- a/src/Image/Image_PixMap.cdl +++ b/src/Image/Image_PixMap.cdl @@ -34,7 +34,8 @@ uses TypeOfImage from Image, HPrivateImage from Image, CRawBufferData from Image, - Color from Quantity + Color from Quantity, + Parameter from Quantity raises PixmapDefinitionError from Aspect, @@ -128,6 +129,19 @@ is -- Note that this function convert input theY coordinate -- to count off from top of an image (while in memory it stored -- upside-down). + + PixelColor ( me : in; + theX, theY : in Integer from Standard; + theAlpha : out Parameter from Quantity ) + returns Color from Quantity; + ---Purpose: + -- Returns the pixel color. This function is relatively slow, + -- use AccessBuffer() instead for stream operations. + -- theAlpha argument is set to color intensity (0 - transparent, 1 - opaque) + -- Note that this function convert input theY coordinate + -- to count off from top of an image (while in memory it stored + -- upside-down). + fields myImage : HPrivateImage from Image is protected; end PixMap; diff --git a/src/Image/Image_PixMap.cxx b/src/Image/Image_PixMap.cxx index a0927fd422..3065861294 100644 --- a/src/Image/Image_PixMap.cxx +++ b/src/Image/Image_PixMap.cxx @@ -120,9 +120,11 @@ } Quantity_Color getPixelColor (const Standard_Integer theCol, - const Standard_Integer theRow) const + const Standard_Integer theRow, + Quantity_Parameter& theAlpha) const { RGBQuad_t* aPixel = (RGBQuad_t* )&getScanLine (theRow)[theCol * myBytesPerPixel]; + theAlpha = (myBytesPerPixel > 3) ? (Standard_Real (aPixel->rgbReserved) / 255.0) : 1.0; return Quantity_Color (Standard_Real (aPixel->rgbRed) / 255.0, Standard_Real (aPixel->rgbGreen) / 255.0, Standard_Real (aPixel->rgbBlue) / 255.0, @@ -414,13 +416,30 @@ void Image_PixMap::AccessBuffer (Image_CRawBufferData& theBuffer) const #endif } +// ======================================================================= +// function : PixelColor +// purpose : +// ======================================================================= Quantity_Color Image_PixMap::PixelColor (const Standard_Integer theX, const Standard_Integer theY) const +{ + Quantity_Parameter aDummy; + return PixelColor (theX, theY, aDummy); +} + +// ======================================================================= +// function : PixelColor +// purpose : +// ======================================================================= +Quantity_Color Image_PixMap::PixelColor (const Standard_Integer theX, + const Standard_Integer theY, + Quantity_Parameter& theAlpha) const { Standard_Integer aScanlineId = myImage->getHeight() - theY - 1; - if (theX < 0 || theX >= (unsigned int)myImage->getWidth() || - theY < 0 || theY >= (unsigned int)myImage->getHeight()) + if (theX < 0 || (unsigned int )theX >= (unsigned int )myImage->getWidth() || + theY < 0 || (unsigned int )theY >= (unsigned int )myImage->getHeight()) { + theAlpha = 0.0; // transparent return Quantity_Color (0.0, 0.0, 0.0, Quantity_TOC_RGB); } #ifdef HAVE_FREEIMAGE @@ -428,6 +447,7 @@ Quantity_Color Image_PixMap::PixelColor (const Standard_Integer theX, { RGBQUAD aValue; memset (&aValue, 0, sizeof(aValue)); myImage->getPixelColor (theX, aScanlineId, &aValue); + theAlpha = (myImage->getColorType() == FIC_RGBALPHA) ? (Standard_Real (aValue.rgbReserved) / 255.0) : 1.0; return Quantity_Color (Standard_Real (aValue.rgbRed) / 255.0, Standard_Real (aValue.rgbGreen) / 255.0, Standard_Real (aValue.rgbBlue) / 255.0, @@ -441,12 +461,14 @@ Quantity_Color Image_PixMap::PixelColor (const Standard_Integer theX, { float* aScanLine = (float* )myImage->getScanLine (aScanlineId); Quantity_Parameter aValue = Quantity_Parameter (aScanLine[theX]); + theAlpha = 1.0; // opaque return Quantity_Color (aValue, aValue, aValue, Quantity_TOC_RGB); } case FIT_RGBF: { FIRGBF* aScanLine = (FIRGBF* )myImage->getScanLine (aScanlineId); FIRGBF* aPixel = &aScanLine[theX]; + theAlpha = 1.0; // opaque return Quantity_Color (Quantity_Parameter (aPixel->red), Quantity_Parameter (aPixel->green), Quantity_Parameter (aPixel->blue), @@ -456,6 +478,7 @@ Quantity_Color Image_PixMap::PixelColor (const Standard_Integer theX, { FIRGBAF* aScanLine = (FIRGBAF* )myImage->getScanLine (aScanlineId); FIRGBAF* aPixel = &aScanLine[theX]; + theAlpha = aPixel->alpha; return Quantity_Color (Quantity_Parameter (aPixel->red), Quantity_Parameter (aPixel->green), Quantity_Parameter (aPixel->blue), @@ -464,11 +487,12 @@ Quantity_Color Image_PixMap::PixelColor (const Standard_Integer theX, default: { // not supported image type + theAlpha = 0.0; // transparent return Quantity_Color (0.0, 0.0, 0.0, Quantity_TOC_RGB); } } } #else - return myImage->getPixelColor (theX, aScanlineId); + return myImage->getPixelColor (theX, aScanlineId, theAlpha); #endif } diff --git a/src/V3d/V3d_View.cxx b/src/V3d/V3d_View.cxx index 4dc97ec514..04df995a20 100755 --- a/src/V3d/V3d_View.cxx +++ b/src/V3d/V3d_View.cxx @@ -3534,15 +3534,16 @@ Handle(Image_PixMap) V3d_View::ToPixMap (const Standard_Integer theWidth, Graphic3d_CView* cView = (Graphic3d_CView* )MyView->CView(); Graphic3d_PtrFrameBuffer aFBOPtr = NULL; Graphic3d_PtrFrameBuffer aPrevFBOPtr = (Graphic3d_PtrFrameBuffer )cView->ptrFBO; + Standard_Integer aFBOVPSizeX (theWidth), aFBOVPSizeY (theHeight), aFBOSizeXMax (0), aFBOSizeYMax (0); Standard_Integer aPrevFBOVPSizeX (0), aPrevFBOVPSizeY (0), aPrevFBOSizeXMax (0), aPrevFBOSizeYMax (0); if (aPrevFBOPtr != NULL) { MyView->FBOGetDimensions (aPrevFBOPtr, aPrevFBOVPSizeX, aPrevFBOVPSizeY, aPrevFBOSizeXMax, aPrevFBOSizeYMax); - if (theWidth <= aPrevFBOSizeXMax && theHeight <= aPrevFBOSizeYMax) + if (aFBOVPSizeX <= aPrevFBOSizeXMax && aFBOVPSizeY <= aPrevFBOSizeYMax) { - MyView->FBOChangeViewport (aPrevFBOPtr, theWidth, theHeight); + MyView->FBOChangeViewport (aPrevFBOPtr, aFBOVPSizeX, aFBOVPSizeY); aFBOPtr = aPrevFBOPtr; } } @@ -3550,7 +3551,17 @@ Handle(Image_PixMap) V3d_View::ToPixMap (const Standard_Integer theWidth, if (aFBOPtr == NULL) { // Try to create hardware accelerated buffer - aFBOPtr = MyView->FBOCreate (theWidth, theHeight); + aFBOPtr = MyView->FBOCreate (aFBOVPSizeX, aFBOVPSizeY); + if (aFBOPtr != NULL) + { + MyView->FBOGetDimensions (aFBOPtr, + aFBOVPSizeX, aFBOVPSizeY, + aFBOSizeXMax, aFBOSizeYMax); + // reduce viewport in case of hardware limits + if (aFBOVPSizeX > aFBOSizeXMax) aFBOVPSizeX = aFBOSizeXMax; + if (aFBOVPSizeY > aFBOSizeYMax) aFBOVPSizeY = aFBOSizeYMax; + MyView->FBOChangeViewport (aFBOPtr, aFBOVPSizeX, aFBOVPSizeY); + } } cView->ptrFBO = aFBOPtr; @@ -3564,7 +3575,7 @@ Handle(Image_PixMap) V3d_View::ToPixMap (const Standard_Integer theWidth, // technically we can reduce existing viewport... // but currently allow only dumping the window itself - if (theWidth != aWinWidth || theHeight != aWinHeight) + if (aFBOVPSizeX != aWinWidth || aFBOVPSizeY != aWinHeight) { return Handle(Image_PixMap)(); } @@ -3583,10 +3594,10 @@ Handle(Image_PixMap) V3d_View::ToPixMap (const Standard_Integer theWidth, //szv: calculate expansion Umin = PUmin; Vmin = PVmin; Umax = PUmax; Vmax = PVmax; Standard_Real oldWidth = (PUmax - PUmin), oldHeight = (PVmax - PVmin); - Standard_Real newWidth = (oldHeight * theWidth) / theHeight; + Standard_Real newWidth = (oldHeight * aFBOVPSizeX) / aFBOVPSizeY; if (newWidth < oldWidth) { - Standard_Real newHeight = (oldWidth * theHeight) / theWidth; + Standard_Real newHeight = (oldWidth * aFBOVPSizeY) / aFBOVPSizeX; // Expand height Standard_Real delta = 0.5 * (newHeight - oldHeight); Vmin = PVmin - delta; @@ -3618,7 +3629,7 @@ Handle(Image_PixMap) V3d_View::ToPixMap (const Standard_Integer theWidth, // allocate image buffer for dumping Image_CRawBufferData aRawBuffer; - Handle(Image_PixMap) anImageBitmap = new Image_PixMap (theWidth, theHeight, theBufferType); + Handle(Image_PixMap) anImageBitmap = new Image_PixMap (aFBOVPSizeX, aFBOVPSizeY, theBufferType); anImageBitmap->AccessBuffer (aRawBuffer); if (!MyView->BufferDump (aRawBuffer)) { diff --git a/src/ViewerTest/ViewerTest.cxx b/src/ViewerTest/ViewerTest.cxx index 85e69b1241..6f13b33758 100755 --- a/src/ViewerTest/ViewerTest.cxx +++ b/src/ViewerTest/ViewerTest.cxx @@ -855,22 +855,42 @@ static Standard_Integer VDump (Draw_Interpretor& di, Standard_Integer argc, cons Handle(AIS_InteractiveContext) IC; Handle(V3d_View) view; GetCtxAndView (IC, view); - if (!view.IsNull()) + if (view.IsNull()) { - if (aWidth > 0 && aHeight > 0) - { - return view->ToPixMap (aWidth, aHeight, aBufferType)->Dump (argv[1]) ? 0 : 1; - } - else - { - return view->Dump (argv[1], aBufferType) ? 0 : 1; - } - } - else - { - di << "Cannot find an active viewer/view" << "\n"; + di << "Cannot find an active viewer/view\n"; return 1; } + + if (aWidth <= 0 || aHeight <= 0) + { + if (!view->Dump (argv[1], aBufferType)) + { + di << "Dumping failed!\n"; + return 1; + } + return 0; + } + + Handle(Image_PixMap) aPixMap = view->ToPixMap (aWidth, aHeight, aBufferType); + if (aPixMap.IsNull()) + { + di << "Dumping failed!\n"; + return 1; + } + + Image_CRawBufferData aRawBuffer; + aPixMap->AccessBuffer (aRawBuffer); + if (aRawBuffer.widthPx != aWidth || aRawBuffer.heightPx != aHeight) + { + std::cout << "Warning! Dumped dimensions " << aRawBuffer.widthPx << "x" << aRawBuffer.heightPx + << " are lesser than requested " << aWidth << "x" << aHeight << "\n"; + } + if (!aPixMap->Dump (argv[1])) + { + di << "Saving image failed!\n"; + return 1; + } + return 0; } diff --git a/src/ViewerTest/ViewerTest_ViewerCommands.cxx b/src/ViewerTest/ViewerTest_ViewerCommands.cxx index b5cd888fe9..530979b9d0 100755 --- a/src/ViewerTest/ViewerTest_ViewerCommands.cxx +++ b/src/ViewerTest/ViewerTest_ViewerCommands.cxx @@ -2817,6 +2817,130 @@ static int VMemGpu (Draw_Interpretor& theDI, return 0; } +// ============================================================================== +// function : VReadPixel +// purpose : +// ============================================================================== +static int VReadPixel (Draw_Interpretor& theDI, + Standard_Integer theArgNb, + const char** theArgVec) +{ + // get the active view + Handle(V3d_View) aView = ViewerTest::CurrentView(); + if (aView.IsNull()) + { + std::cerr << "No active view. Please call vinit.\n"; + return 1; + } + else if (theArgNb < 3) + { + std::cerr << "Usage : " << theArgVec[0] << " xPixel yPixel [{rgb|rgba|depth|hls|rgbf|rgbaf}=rgba] [name]\n"; + return 1; + } + + Image_TypeOfImage aBufferType = Image_TOI_RGBA; + Standard_Integer aWidth, aHeight; + aView->Window()->Size (aWidth, aHeight); + const Standard_Integer anX = atoi (theArgVec[1]); + const Standard_Integer anY = atoi (theArgVec[2]); + if (anX < 0 || anX >= aWidth || anY < 0 || anY > aHeight) + { + std::cerr << "Pixel coordinates (" << anX << "; " << anY << ") are out of view (" << aWidth << " x " << aHeight << ")\n"; + return 1; + } + + Standard_Boolean toShowName = Standard_False; + Standard_Boolean toShowHls = Standard_False; + for (Standard_Integer anIter = 3; anIter < theArgNb; ++anIter) + { + TCollection_AsciiString aParam (theArgVec[anIter]); + if (TCollection_AsciiString::ISSIMILAR (aParam, TCollection_AsciiString ("rgb"))) + { + aBufferType = Image_TOI_RGB; + } + else if (TCollection_AsciiString::ISSIMILAR (aParam, TCollection_AsciiString ("hls"))) + { + aBufferType = Image_TOI_RGB; + toShowHls = Standard_True; + } + else if (TCollection_AsciiString::ISSIMILAR (aParam, TCollection_AsciiString ("rgbf"))) + { + aBufferType = Image_TOI_RGBF; + } + else if (TCollection_AsciiString::ISSIMILAR (aParam, TCollection_AsciiString ("rgba"))) + { + aBufferType = Image_TOI_RGBA; + } + else if (TCollection_AsciiString::ISSIMILAR (aParam, TCollection_AsciiString ("rgbaf"))) + { + aBufferType = Image_TOI_RGBAF; + } + else if (TCollection_AsciiString::ISSIMILAR (aParam, TCollection_AsciiString ("depth"))) + { + aBufferType = Image_TOI_FLOAT; + } + else if (TCollection_AsciiString::ISSIMILAR (aParam, TCollection_AsciiString ("name"))) + { + toShowName = Standard_True; + } + } + + Handle(Image_PixMap) anImage = aView->ToPixMap (aWidth, aHeight, aBufferType); + if (anImage.IsNull()) + { + std::cerr << "Image dump failed\n"; + return 1; + } + + Quantity_Parameter anAlpha; + Quantity_Color aColor = anImage->PixelColor (anX, anY, anAlpha); + if (toShowName) + { + if (aBufferType == Image_TOI_RGBA + || aBufferType == Image_TOI_RGBAF) + { + theDI << Quantity_Color::StringName (aColor.Name()) << " " << anAlpha << "\n"; + } + else + { + theDI << Quantity_Color::StringName (aColor.Name()) << "\n"; + } + } + else + { + switch (aBufferType) + { + default: + case Image_TOI_RGB: + case Image_TOI_RGBF: + { + if (toShowHls) + { + theDI << aColor.Hue() << " " << aColor.Light() << " " << aColor.Saturation() << "\n"; + } + else + { + theDI << aColor.Red() << " " << aColor.Green() << " " << aColor.Blue() << "\n"; + } + break; + } + case Image_TOI_RGBA: + case Image_TOI_RGBAF: + { + theDI << aColor.Red() << " " << aColor.Green() << " " << aColor.Blue() << " " << anAlpha << "\n"; + break; + } + case Image_TOI_FLOAT: + { + theDI << aColor.Red() << "\n"; + break; + } + } + } + + return 0; +} + //======================================================================= //function : ViewerCommands //purpose : @@ -2928,4 +3052,8 @@ void ViewerTest::ViewerCommands(Draw_Interpretor& theCommands) "vmemgpu [f]: print system-dependent GPU memory information if available;" " with f option returns free memory in bytes", __FILE__, VMemGpu, group); + theCommands.Add ("vreadpixel", + "vreadpixel xPixel yPixel [{rgb|rgba|depth|hls|rgbf|rgbaf}=rgba] [name]" + " : Read pixel value for active view", + __FILE__, VReadPixel, group); }