/** * @brief * Constructor * * @param[in] cOldImageBuffer * Old image buffer (MUST have valid data) * @param[out] cImageBuffer * Image buffer (MUST have valid data) * @param[in] nNewWidth * New width * @param[in] nNewHeight * New height * @param[in] nOldWidth * Old height * @param[in] nOldHeight * Old height * @param[in] mFilter * Filter matrix */ ScaleDownData(const ImageBuffer &cOldImageBuffer, ImageBuffer &cImageBuffer, uint32 nNewWidth, uint32 nNewHeight, uint32 nOldWidth, uint32 nOldHeight, const Matrix3x3 &mFilter) { // [TODO] Resize images in linear space instead of gamma space! Maybe we should add image space information to a image so we now in which space the image data is stored? (linear, gamma...) // Scale factors const float fToOriginalWidthFactor = static_cast<float>(nOldWidth) /nNewWidth; const float fToOriginalHeightFactor = static_cast<float>(nOldHeight)/nNewHeight; // Get the number of components const uint32 nNumOfComponents = cOldImageBuffer.GetComponentsPerPixel(); // Loop through all components DataType *pNewData = reinterpret_cast<DataType*>(cImageBuffer.GetData()); const DataType *pOldData = reinterpret_cast<const DataType*>(cOldImageBuffer.GetData()); for (uint32 nComponent=0; nComponent<nNumOfComponents; nComponent++) { // Loop through x for (uint32 nX=0; nX<nNewWidth; nX++) { // Loop through y for (uint32 nY=0; nY<nNewHeight; nY++) { // Get the original byte const uint32 nOriginalX = static_cast<uint32>(nX*fToOriginalWidthFactor); const uint32 nOriginalY = static_cast<uint32>(nY*fToOriginalHeightFactor); // Sum up double fSum = 0.0f; double fTotalSum = 0.0f; for (uint32 nFilterX=0; nFilterX<3; nFilterX++) { for (uint32 nFilterY=0; nFilterY<3; nFilterY++) { // Get the current position on the original image const int nCurrentX = (nOriginalX-1) + nFilterX; const int nCurrentY = (nOriginalY-1) + nFilterY; // Is this current position inside the original image? if (nCurrentX >= 0 && nCurrentY >= 0 && nCurrentX < static_cast<int>(nOldWidth) && nCurrentY < static_cast<int>(nOldHeight)) { // Jap, get the factor to use this component with const float fFactor = mFilter.fM33[nFilterX][nFilterY]; // Now we've got one more component within our sum fTotalSum += fFactor; // Get the component value of the original image const DataType nOriginalByte = pOldData[(nCurrentY*nOldWidth + nCurrentX)*nNumOfComponents + nComponent]; // Add component value of the original image with the given factor fSum += nOriginalByte*fFactor; } } } // Set new byte pNewData[(nY*nNewWidth + nX)*nNumOfComponents + nComponent] = DataType(fSum/fTotalSum); } } } }
//[-------------------------------------------------------] //[ Private virtual TextureCreator functions ] //[-------------------------------------------------------] Texture *TextureCreatorSpot2D::Create(TextureManager &cTextureManager, Texture *pTexture) const { // Create the texture pTexture = CreateTexture(cTextureManager, pTexture); // Create the image Image cImage = Image::CreateImage(DataByte, ColorGrayscale, Vector3i(Width, Height, 1)); ImageBuffer *pImageBuffer = cImage.GetBuffer(); // Create the texture data const float fWidthHalf = static_cast<float>(Width/2)-0.5f; const float fHeightHalf = static_cast<float>(Height/2)-0.5f; uint8 *pData = pImageBuffer->GetData(); for (uint32 j=0; j<Width; j++) { for (uint32 i=0; i<Height; i++) { const float x = (i - fWidthHalf)/fWidthHalf; const float y = (j - fHeightHalf)/fHeightHalf; const float ls = Math::Max(1 - (x*x + y*y), 0.0f); pData[j*Width+i] = static_cast<uint8>(Brightness*ls); } } // Create the 2D texture buffer pTexture->SetTextureBuffer(reinterpret_cast<TextureBuffer*>(cTextureManager.GetRendererContext().GetRenderer().CreateTextureBuffer2D(cImage))); // Return the created texture return pTexture; }
bool ImageLoaderJPG::LoadParams(Image &cImage, File &cFile, bool bBlockSmoothing, bool bFancyUpsampling) { jpeg_decompress_struct sInfo; jpeg_error_mgr sError; sInfo.err = jpeg_std_error(&sError); sInfo.err->error_exit = ExitErrorHandle; jpeg_create_decompress(&sInfo); // Set the user given parameters sInfo.do_block_smoothing = bBlockSmoothing; sInfo.do_fancy_upsampling = bFancyUpsampling; jpeg_read_init(&sInfo, &cFile); jpeg_read_header(&sInfo, TRUE); jpeg_start_decompress(&sInfo); // Get the color format EColorFormat nColorFormat; switch (sInfo.num_components) { case 1: nColorFormat = ColorGrayscale; break; case 3: nColorFormat = ColorRGB; break; case 4: nColorFormat = ColorRGBA; break; default: // Error: Unsupported color format return false; } // Create image buffer ImageBuffer *pImageBuffer = cImage.CreatePart()->CreateMipmap(); pImageBuffer->CreateImage(DataByte, nColorFormat, Vector3i(sInfo.output_width, sInfo.output_height, 1)); // Read in the data uint8 *pCurrentData = pImageBuffer->GetData(); while (sInfo.output_scanline < sInfo.output_height) { jpeg_read_scanlines(&sInfo, &pCurrentData, 1); pCurrentData += pImageBuffer->GetBytesPerRow(); } // Cleanup jpeg_finish_decompress(&sInfo); jpeg_destroy_decompress(&sInfo); // Done return true; }
//[-------------------------------------------------------] //[ Public RTTI methods ] //[-------------------------------------------------------] bool TransferFunctionLoaderTABLE::Load(TransferFunction &cTransferFunction, File &cFile) { // Get the image holding the transfer function Image &cImage = cTransferFunction.GetImage(); // A "table"-file (simple ASCII) looks like this /* 1 1 1 1 baseOpacity: 0.698039 NoTags 0 0 0 0 . . . . . . . . . . . . */ // Use the tokenizer in order to gather all required information, ignore the rest // Startup the tokenizer Tokenizer cTokenizer; cTokenizer.Start(cFile.GetContentAsString()); // Ignore the first line ("1 1 1 1") cTokenizer.GetNextToken(); cTokenizer.GetNextToken(); cTokenizer.GetNextToken(); cTokenizer.GetNextToken(); // Ignore base opacity ("baseOpacity: 0.698039") cTokenizer.GetNextToken(); cTokenizer.GetNextToken(); // Ignore tags to keep this loader simple ("NoTags") cTokenizer.GetNextToken(); // Create image buffer ImageBuffer *pImageBuffer = cImage.CreatePart()->CreateMipmap(); pImageBuffer->CreateImage(DataByte, ColorRGBA, Vector3i(256, 1, 1)); // Read in the palette uint8 *pImageData = pImageBuffer->GetData(); for (uint32 i=0; i<256; i++) { // Read RGBA entry *pImageData = cTokenizer.GetNextToken().GetUInt8(); pImageData++; *pImageData = cTokenizer.GetNextToken().GetUInt8(); pImageData++; *pImageData = cTokenizer.GetNextToken().GetUInt8(); pImageData++; *pImageData = cTokenizer.GetNextToken().GetUInt8(); pImageData++; } // Done return true; }
//[-------------------------------------------------------] //[ Global functions ] //[-------------------------------------------------------] // [TODO] This is just an experimental half image scale function without any filter - better as nothing for now! void ScaleDownHalfData(const ImageBuffer &cOldImageBuffer, ImageBuffer &cImageBuffer, uint32 nNewWidth, uint32 nNewHeight, uint32 nOldWidth, uint32 nOldHeight, const Matrix3x3 &mFilter) { // Scale factors const float fToOriginalWidthFactor = static_cast<float>(nOldWidth) /nNewWidth; const float fToOriginalHeightFactor = static_cast<float>(nOldHeight)/nNewHeight; // Get the number of components const uint32 nNumOfComponents = cOldImageBuffer.GetComponentsPerPixel(); // Loop through all components short *pNewData = reinterpret_cast<short*>(cImageBuffer.GetData()); const short *pOldData = reinterpret_cast<const short*>(cOldImageBuffer.GetData()); for (uint32 nComponent=0; nComponent<nNumOfComponents; nComponent++) { // Loop through x for (uint32 nX=0; nX<nNewWidth; nX++) { // Loop through y for (uint32 nY=0; nY<nNewHeight; nY++) { // Get the original byte const uint32 nOriginalX = static_cast<uint32>(nX*fToOriginalWidthFactor); const uint32 nOriginalY = static_cast<uint32>(nY*fToOriginalHeightFactor); // Get data short nOriginalByte = 0; { // Get the current position on the original image const int nCurrentX = (nOriginalX-1) + 1; const int nCurrentY = (nOriginalY-1) + 1; // Is this current position inside the original image? if (nCurrentX >= 0 && nCurrentY >= 0 && nCurrentX < static_cast<int>(nOldWidth) && nCurrentY < static_cast<int>(nOldHeight)) { // Get the component value of the original image nOriginalByte = pOldData[(nCurrentY*nOldWidth + nCurrentX)*nNumOfComponents + nComponent]; } } // Set new byte pNewData[(nY*nNewWidth + nX)*nNumOfComponents + nComponent] = nOriginalByte; } } } }
bool ImageLoaderPVM::Save(const Image &cImage, File &cFile) { // Get image buffer, we only support the data type "byte" and "word" ImageBuffer *pImageBuffer = cImage.GetBuffer(); if (pImageBuffer && (pImageBuffer->GetDataFormat() == DataByte || pImageBuffer->GetDataFormat() == DataWord)) { // Save the data const Vector3i &vSize = pImageBuffer->GetSize(); writePVMvolume(cFile.GetUrl().GetNativePath().GetASCII(), pImageBuffer->GetData(), vSize.x, vSize.y, vSize.z, (pImageBuffer->GetDataFormat() == DataByte) ? 1 : 2); // Done return true; } // Error! return false; }
bool VolumeLoaderDAT::Save(const Volume &cVolume, File &cFile) { // Get the image holding the volumetric data const Image &cImage = cVolume.GetVolumeImage(); // Get image buffer, we only support the data type "byte" and "word" ImageBuffer *pImageBuffer = cImage.GetBuffer(); if (pImageBuffer && (pImageBuffer->GetDataFormat() == DataByte || pImageBuffer->GetDataFormat() == DataWord)) { const Vector3i &vSize = pImageBuffer->GetSize(); // Construct the object filename const String sObjectFilename = cFile.GetUrl().GetTitle() + ".raw"; // A "dat"-file has simple ASCII content cFile.PutS("ObjectFileName: " + sObjectFilename + '\n'); // Example: "ObjectFileName: Teddybear.raw" cFile.PutS("TaggedFileName: ---\n"); // Example: "TaggedFileName: ---" cFile.PutS("Resolution: " + vSize.ToString() + '\n'); // Example: "Resolution: 128 128 62" cFile.PutS("SliceThickness: 1 1 1\n"); // Example: "SliceThickness: 2.8 2.8 5" if (pImageBuffer->GetDataFormat() == DataByte) // Example: "Format: UCHAR" cFile.PutS("Format: UCHAR\n"); else cFile.PutS("Format: USHORT\n"); cFile.PutS("NbrTags: 0\n"); // Example: "NbrTags: 0" cFile.PutS("ObjectType: TEXTURE_VOLUME_OBJECT\n"); // Example: "ObjectType: TEXTURE_VOLUME_OBJECT" cFile.PutS("ObjectModel: RGBA\n"); // Example: "ObjectModel: RGBA" cFile.PutS("GridType: EQUIDISTANT\n"); // Example: "GridType: EQUIDISTANT" { // Raw data... // Get the absolute filename of the raw file const String sFilename = cFile.GetUrl().CutFilename() + sObjectFilename; // Open the raw file File cRawFile(sFilename); if (cRawFile.Open(File::FileCreate | File::FileWrite)) { // Save the data cRawFile.Write(pImageBuffer->GetData(), 1, pImageBuffer->GetDataSize()); // Done return true; } } } // Error! return false; }
//[-------------------------------------------------------] //[ Private virtual TextureCreator functions ] //[-------------------------------------------------------] Texture *TextureCreatorTurbulence3D::Create(TextureManager &cTextureManager, Texture *pTexture) const { // Create the texture pTexture = CreateTexture(cTextureManager, pTexture); // Get scale const Vector3 &vScale = Scale.Get(); // Create the image Image cImage = Image::CreateImage(DataByte, ColorGrayscale, Vector3i(XSize, YSize, ZSize)); ImageBuffer *pImageBuffer = cImage.GetBuffer(); // Create the buffer uint8 *pTurbBuffer = pImageBuffer->GetData(); uint8 *pDest = pTurbBuffer; uint32 nMin = 255, nMax = 0; for (uint32 nZ=0; nZ<ZSize; nZ++) { for (uint32 nY=0; nY<YSize; nY++) { for (uint32 nX=0; nX<XSize; nX++) { const uint8 nT = static_cast<uint8>(127.5f*(1 + PerlinNoiseTurbulence::TileableTurbulence3(vScale.x*nX, vScale.y*nY, vScale.z*nZ, XSize*vScale.x, YSize*vScale.y, ZSize*vScale.z, 16))); if (nT > nMax) nMax = nT; if (nT < nMin) nMin = nT; *pDest++ = nT; } } } const uint32 nNumOfPixels = XSize*YSize*ZSize; pDest = pTurbBuffer; for (uint32 i=0; i<nNumOfPixels; i++, pDest++) *pDest = static_cast<uint8>((255*(*pDest - nMin))/(nMax - nMin)); // Create the 3D texture buffer pTexture->SetTextureBuffer(reinterpret_cast<TextureBuffer*>(cTextureManager.GetRendererContext().GetRenderer().CreateTextureBuffer3D(cImage))); // Return the created texture return pTexture; }
//[-------------------------------------------------------] //[ Private virtual PLScene::SceneNode functions ] //[-------------------------------------------------------] void PGImage::InitFunction() { // Call base implementation SNParticleGroup::InitFunction(); // Load image Image cImage; if (cImage.LoadByFilename(ImageFilename.Get())) { // Get the image buffer ImageBuffer *pImageBuffer = cImage.GetBuffer(); if (pImageBuffer) { const uint32 nWidth = pImageBuffer->GetSize().x; const uint32 nHeight = pImageBuffer->GetSize().y; const uint32 nPixels = nWidth*nHeight; const uint8 *pData = pImageBuffer->GetData(); const uint32 nColorComponents = pImageBuffer->GetComponentsPerPixel(); if (nColorComponents == 3 || nColorComponents == 4) { // Get total number of required particles uint32 nParticles = 0; for (uint32 i=0; i<nPixels; i++) { // Place particle at this image position? uint8 nDifR = static_cast<uint8>(Math::Abs(pData[0]-RedColorKey)); uint8 nDifG = static_cast<uint8>(Math::Abs(pData[1]-GreenColorKey)); uint8 nDifB = static_cast<uint8>(Math::Abs(pData[2]-BlueColorKey)); if (!(nDifR <= ColorKeyTolerance && nDifG <= ColorKeyTolerance && nDifB <= ColorKeyTolerance)) nParticles++; // A particle here, please pData += nColorComponents; } pData = pImageBuffer->GetData(); // Create the particles InitParticles(nParticles); // Setup the particles for (int nY=nHeight-1; nY>=0; nY--) { for (uint32 nX=0; nX<nWidth; nX++) { // Place particle at this image position? uint8 nDifR = static_cast<uint8>(Math::Abs(pData[0]-RedColorKey)); uint8 nDifG = static_cast<uint8>(Math::Abs(pData[1]-GreenColorKey)); uint8 nDifB = static_cast<uint8>(Math::Abs(pData[2]-BlueColorKey)); if (nDifR <= ColorKeyTolerance && nDifG <= ColorKeyTolerance && nDifB <= ColorKeyTolerance) { // No particle here, please pData += nColorComponents; } else { // Setup particle Particle *pParticle = AddParticle(); if (pParticle) { pParticle->vColor.r = pData[0]/255.0f; pParticle->vColor.g = pData[1]/255.0f; pParticle->vColor.b = pData[2]/255.0f; pParticle->vColor.a = nColorComponents == 4 ? pData[3]/255.0f : 1.0f; pData += nColorComponents; pParticle->fEnergy = 1.0f; pParticle->fCustom2 = 0.5f+Math::GetRandFloat()*2.0f; if (Math::GetRandFloat() > 0.5f) pParticle->fCustom2 = -pParticle->fCustom2; pParticle->fSize = (1.0f+(static_cast<float>(Math::GetRand() % 1000)/500))*ImageScale; pParticle->vPos.x = pParticle->vFixPos.x = pParticle->vDistortion.x = static_cast<float>(nX*ImageScale); pParticle->vPos.y = pParticle->vFixPos.y = pParticle->vDistortion.y = static_cast<float>(nY*ImageScale); pParticle->vPos.z = pParticle->vFixPos.z = pParticle->vDistortion.z = 0.0f; pParticle->fCustom1 = pParticle->fSize; pParticle->fCustom2 *= 2; } else { // Error! nY = nHeight; nX = nWidth; } } } } } } } }
bool ImageLoaderJPG::SaveParams(const Image &cImage, File &cFile, uint32 nQuality) { // Get the image buffer ImageBuffer *pImageBuffer = cImage.GetBuffer(); if (pImageBuffer && pImageBuffer->GetBytesPerRow()) { // We only support 1 or 3 byte per pixel component if (pImageBuffer->GetBytesPerPixelComponent() == 1 || pImageBuffer->GetBytesPerPixelComponent() == 3) { jpeg_compress_struct sInfo; jpeg_error_mgr sError; sInfo.err = jpeg_std_error(&sError); sInfo.err->error_exit = ExitErrorHandle; jpeg_create_compress(&sInfo); const int nComponents = pImageBuffer->GetComponentsPerPixel(); sInfo.in_color_space = (nComponents == 1)? JCS_GRAYSCALE : JCS_RGB; jpeg_set_defaults(&sInfo); sInfo.input_components = nComponents; sInfo.num_components = (nComponents == 1) ? 1 : 3; sInfo.image_width = pImageBuffer->GetSize().x; sInfo.image_height = pImageBuffer->GetSize().y; sInfo.data_precision = 8; sInfo.input_gamma = 1.0; // Set the user given parameter jpeg_set_quality(&sInfo, nQuality, FALSE); jpeg_write_init(&sInfo, &cFile); jpeg_start_compress(&sInfo, TRUE); // Is the input image RGBA? If so, we really need to throw away the alpha channel... if (nComponents == 4) { // Allocate memory for an converted output row uint8 *pOutputRow = new uint8[sInfo.image_width*sInfo.num_components]; // Write the data const uint8 *pCurrentImageData = pImageBuffer->GetData(); for (uint32 y=0; y<sInfo.image_height; y++) { // Convert the current row for (uint32 x=0; x<sInfo.image_width; x++) { const uint8 *pnPixelIn = &pCurrentImageData[x*4]; uint8 *pnPixelOut = &pOutputRow[x*3]; pnPixelOut[0] = pnPixelIn[0]; pnPixelOut[1] = pnPixelIn[1]; pnPixelOut[2] = pnPixelIn[2]; } // Write out the current row jpeg_write_scanlines(&sInfo, &pOutputRow, 1); // Next, please pCurrentImageData += pImageBuffer->GetBytesPerRow(); } // Free the allocated output row memory delete [] pOutputRow; } else { // Write the data uint8 *pCurrentImageData = pImageBuffer->GetData(); for (uint32 y=0; y<sInfo.image_height; y++) { jpeg_write_scanlines(&sInfo, &pCurrentImageData, 1); pCurrentImageData += pImageBuffer->GetBytesPerRow(); } } // Cleanup jpeg_finish_compress(&sInfo); jpeg_destroy_compress(&sInfo); // Done return true; } else { // Error: Unsupported number of bytes per pixel component } } else { // Error: Failed to get image buffer } // Error! return false; }
//[-------------------------------------------------------] //[ Public RTTI methods ] //[-------------------------------------------------------] bool VolumeLoaderDAT::Load(Volume &cVolume, File &cFile) { Url cObjectFilename; Vector3i vResolution; EDataFormat nFormat = DataByte; // Get the image holding the volumetric data Image &cImage = cVolume.GetVolumeImage(); { // Use the tokenizer in order to gather all required information, ignore the rest // A "dat"-file (simple ASCII) looks like this /* ObjectFileName: Teddybear.raw TaggedFileName: --- Resolution: 128 128 62 SliceThickness: 2.8 2.8 5 Format: UCHAR NbrTags: 0 ObjectType: TEXTURE_VOLUME_OBJECT ObjectModel: RGBA GridType: EQUIDISTANT */ // Startup the tokenizer Tokenizer cTokenizer; cTokenizer.Start(cFile.GetContentAsString()); // Loop through all tokens String sToken = cTokenizer.GetNextToken(); while (sToken.GetLength()) { // ObjectFileName if (sToken == "ObjectFileName:") { // The file format specification says: // "The object file name refers to the name of the data file, which contains the raw voxel data. // This can be either an absolute path or a relative path with respect to the storage position of the dat file." // Get the value cObjectFilename = cTokenizer.GetNextToken(); // Resolution } else if (sToken == "Resolution:") { // The file format specification says: // "The volume data set consists of a large array of voxel values. The resolution of the data set is given // by the number of voxels in x-, y- and z- direction." // Get the values vResolution.x = cTokenizer.GetNextToken().GetInt(); vResolution.y = cTokenizer.GetNextToken().GetInt(); vResolution.z = cTokenizer.GetNextToken().GetInt(); // SliceThickness } else if (sToken == "SliceThickness:") { // The file format specification says: // "The size of one voxel in x-, y- and z- direction (usually in millimeters)." // mm to cm cm to m static const float Scale = 0.1f * 0.01f; // Get the values Vector3 vSliceThickness; vSliceThickness.x = cTokenizer.GetNextToken().GetFloat()*Scale; vSliceThickness.y = cTokenizer.GetNextToken().GetFloat()*Scale; vSliceThickness.z = cTokenizer.GetNextToken().GetFloat()*Scale; // Set the size of one voxel (without metric, but usually one unit is equal to one meter) cVolume.SetVoxelSize(vSliceThickness); // Format } else if (sToken == "Format:") { // The file format specification says: // "The data format of one voxel. Can be either UCHAR (8 bit) or USHORT (16 bit)." // Get the value const String sFormat = cTokenizer.GetNextToken(); if (sFormat == "UCHAR") nFormat = DataByte; // Byte (8 bit) else if (sFormat == "USHORT") nFormat = DataWord; // Word (16 bit) } // Next token, please sToken = cTokenizer.GetNextToken(); } } // Valid settings? If so, open and read in the raw data... if (!cObjectFilename.IsEmpty() && vResolution.x > 0 && vResolution.y > 0 && vResolution.z > 0) { // The file format specification says: // "The data file simply contains the raw voxel data as a large binary array which is indexed as // voxel(x,y,z) = array[z * YDIM * XDIM + y * XDIM + x], // with XDIM, YDIM and ZDIM referring to the resolution of the data set, as specified in line 3 // of the header file. For 16 bit data, the data may be stored either in big endian or little endian format." // -> We expect "Little Endian First" // Get the filename of the raw file const String sRawFilename = cObjectFilename.IsAbsolute() ? cObjectFilename.GetUrl() : (cFile.GetUrl().CutFilename() + cObjectFilename.GetUrl()); // Open the raw file File cRawFile(sRawFilename); if (cRawFile.Open(File::FileRead)) { // Create image buffer ImageBuffer *pImageBuffer = cImage.CreatePart()->CreateMipmap(); pImageBuffer->CreateImage(nFormat, ColorGrayscale, vResolution); // Read the data cRawFile.Read(pImageBuffer->GetData(), 1, pImageBuffer->GetDataSize()); // Load the transfer function by using "<filename>.table", or at least try it cVolume.GetTransferFunction().LoadByFilename(cFile.GetUrl().CutExtension() + ".table"); // Done return true; } } // Error! return false; }