ezResult ezTexConv2::WriteTexFile(ezStreamWriter& stream, const ezImage& image) { ezAssetFileHeader asset; asset.SetFileHashAndVersion(m_Processor.m_Descriptor.m_uiAssetHash, m_Processor.m_Descriptor.m_uiAssetVersion); asset.Write(stream); ezTexFormat texFormat; texFormat.m_bSRGB = ezImageFormat::IsSrgb(image.GetImageFormat()); texFormat.m_AddressModeU = m_Processor.m_Descriptor.m_AddressModeU; texFormat.m_AddressModeV = m_Processor.m_Descriptor.m_AddressModeV; texFormat.m_AddressModeW = m_Processor.m_Descriptor.m_AddressModeW; texFormat.m_TextureFilter = m_Processor.m_Descriptor.m_FilterMode; texFormat.WriteTextureHeader(stream); ezDdsFileFormat ddsWriter; if (ddsWriter.WriteImage(stream, image, ezLog::GetThreadLocalLogSystem(), "dds").Failed()) { ezLog::Error("Failed to write DDS image chunk to ezTex file."); return EZ_FAILURE; } return EZ_SUCCESS; }
ezResult ezImageConversionBase::Convert(const ezImage& source, ezImage& target, ezImageFormat::Enum targetFormat) { ezImageFormat::Enum sourceFormat = source.GetImageFormat(); // Trivial copy if (sourceFormat == targetFormat) { target = source; return EZ_SUCCESS; } if (!s_bConversionTableValid) { RebuildConversionTable(); } ezUInt32 uiCurrentTableIndex = GetTableIndex(sourceFormat, targetFormat); // No conversion known if (s_conversionTable[uiCurrentTableIndex] == nullptr) { return EZ_FAILURE; } const ezImageConversionBase* pConversion = s_conversionTable[uiCurrentTableIndex]; const SubConversion& subConversion = pConversion->m_subConversions[s_subConversionTable[uiCurrentTableIndex]]; if (subConversion.m_targetFormat == targetFormat) { if (&source == &target && !subConversion.m_flags.IsSet(ezImageConversionFlags::InPlace)) { ezImage copy = source; return pConversion->DoConvert(copy, target, subConversion.m_targetFormat); } else { return pConversion->DoConvert(source, target, subConversion.m_targetFormat); } } else { ezImage intermediate; if (pConversion->DoConvert(source, intermediate, subConversion.m_targetFormat) == EZ_FAILURE) { return EZ_FAILURE; } return Convert(intermediate, target, targetFormat); } }
ezResult ezBmpFileFormat::WriteImage(ezStreamWriterBase& stream, const ezImage& image, ezLogInterface* pLog) const { // Technically almost arbitrary formats are supported, but we only use the common ones. ezImageFormat::Enum compatibleFormats[] = { ezImageFormat::B8G8R8X8_UNORM, ezImageFormat::B8G8R8A8_UNORM, ezImageFormat::B8G8R8_UNORM, ezImageFormat::B5G5R5X1_UNORM, ezImageFormat::B5G6R5_UNORM, }; // Find a compatible format closest to the one the image currently has ezImageFormat::Enum format = ezImageConversionBase::FindClosestCompatibleFormat(image.GetImageFormat(), compatibleFormats); if (format == ezImageFormat::UNKNOWN) { ezLog::Error(pLog, "No conversion from format '%s' to a format suitable for BMP files known.", ezImageFormat::GetName(image.GetImageFormat())); return EZ_FAILURE; } // Convert if not already in a compatible format if (format != image.GetImageFormat()) { ezImage convertedImage; if (ezImageConversionBase::Convert(image, convertedImage, format) != EZ_SUCCESS) { // This should never happen EZ_ASSERT_DEV(false, "ezImageConversion::Convert failed even though the conversion was to the format returned by FindClosestCompatibleFormat."); return EZ_FAILURE; } return WriteImage(stream, convertedImage, pLog); } ezUInt32 uiRowPitch = image.GetRowPitch(0); ezUInt32 uiHeight = image.GetHeight(0); int dataSize = uiRowPitch * uiHeight; ezBmpFileInfoHeader fileInfoHeader; fileInfoHeader.m_width = image.GetWidth(0); fileInfoHeader.m_height = uiHeight; fileInfoHeader.m_planes = 1; fileInfoHeader.m_bitCount = ezImageFormat::GetBitsPerPixel(format); fileInfoHeader.m_sizeImage = 0; // Can be zero unless we store the data compressed fileInfoHeader.m_xPelsPerMeter = 0; fileInfoHeader.m_yPelsPerMeter = 0; fileInfoHeader.m_clrUsed = 0; fileInfoHeader.m_clrImportant = 0; bool bWriteColorMask = false; // Prefer to write a V3 header ezUInt32 uiHeaderVersion = 3; switch (format) { case ezImageFormat::B8G8R8X8_UNORM: case ezImageFormat::B5G5R5X1_UNORM: case ezImageFormat::B8G8R8_UNORM: fileInfoHeader.m_compression = RGB; break; case ezImageFormat::B8G8R8A8_UNORM: fileInfoHeader.m_compression = BITFIELDS; uiHeaderVersion = 4; break; case ezImageFormat::B5G6R5_UNORM: fileInfoHeader.m_compression = BITFIELDS; bWriteColorMask = true; break; default: return EZ_FAILURE; } EZ_ASSERT_DEV(!bWriteColorMask || uiHeaderVersion <= 3, "Internal bug"); ezUInt32 uiFileInfoHeaderSize = sizeof(ezBmpFileInfoHeader); ezUInt32 uiHeaderSize = sizeof(ezBmpFileHeader); if (uiHeaderVersion >= 4) { uiFileInfoHeaderSize += sizeof(ezBmpFileInfoHeaderV4); } else if (bWriteColorMask) { uiHeaderSize += 3 * sizeof(ezUInt32); } uiHeaderSize += uiFileInfoHeaderSize; fileInfoHeader.m_size = uiFileInfoHeaderSize; ezBmpFileHeader header; header.m_type = ezBmpFileMagic; header.m_size = uiHeaderSize + dataSize; header.m_reserved1 = 0; header.m_reserved2 = 0; header.m_offBits = uiHeaderSize; const void* dataPtr = image.GetDataPointer<void>(); // Write all data if (stream.WriteBytes(&header, sizeof(header)) != EZ_SUCCESS) { ezLog::Error(pLog, "Failed to write header."); return EZ_FAILURE; } if (stream.WriteBytes(&fileInfoHeader, sizeof(fileInfoHeader)) != EZ_SUCCESS) { ezLog::Error(pLog, "Failed to write fileInfoHeader."); return EZ_FAILURE; } if (uiHeaderVersion >= 4) { ezBmpFileInfoHeaderV4 fileInfoHeaderV4; memset(&fileInfoHeaderV4, 0, sizeof(fileInfoHeaderV4)); fileInfoHeaderV4.m_redMask = ezImageFormat::GetRedMask(format); fileInfoHeaderV4.m_greenMask = ezImageFormat::GetGreenMask(format); fileInfoHeaderV4.m_blueMask = ezImageFormat::GetBlueMask(format); fileInfoHeaderV4.m_alphaMask = ezImageFormat::GetAlphaMask(format); if (stream.WriteBytes(&fileInfoHeaderV4, sizeof(fileInfoHeaderV4)) != EZ_SUCCESS) { ezLog::Error(pLog, "Failed to write fileInfoHeaderV4."); return EZ_FAILURE; } } else if (bWriteColorMask) { struct { ezUInt32 m_red; ezUInt32 m_green; ezUInt32 m_blue; } colorMask; colorMask.m_red = ezImageFormat::GetRedMask(format); colorMask.m_green = ezImageFormat::GetGreenMask(format); colorMask.m_blue = ezImageFormat::GetBlueMask(format); if (stream.WriteBytes(&colorMask, sizeof(colorMask)) != EZ_SUCCESS) { ezLog::Error(pLog, "Failed to write colorMask."); return EZ_FAILURE; } } const ezUInt32 uiPaddedRowPitch = ((uiRowPitch - 1) / 4 + 1) * 4; // Write rows in reverse order for (ezInt32 iRow = uiHeight - 1; iRow >= 0; iRow--) { if (stream.WriteBytes(image.GetPixelPointer<void>(0, 0, 0, 0, iRow, 0), uiPaddedRowPitch) != EZ_SUCCESS) { ezLog::Error(pLog, "Failed to write data."); return EZ_FAILURE; } } return EZ_SUCCESS; }