/** * @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); } } } }
//[-------------------------------------------------------] //[ 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; } } } }
//[-------------------------------------------------------] //[ 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; }