bool CImageWriterBMP::writeImage(io::IWriteFile* file, IImage* image, u32 param) const { // we always write 24-bit color because nothing really reads 32-bit SBMPHeader imageHeader; imageHeader.Id = 0x4d42; imageHeader.Reserved = 0; imageHeader.BitmapDataOffset = sizeof(imageHeader); imageHeader.BitmapHeaderSize = 0x28; imageHeader.Width = image->getDimension().Width; imageHeader.Height = image->getDimension().Height; imageHeader.Planes = 1; imageHeader.BPP = 24; imageHeader.Compression = 0; imageHeader.PixelPerMeterX = 0; imageHeader.PixelPerMeterY = 0; imageHeader.Colors = 0; imageHeader.ImportantColors = 0; // data size is rounded up to next larger 4 bytes boundary imageHeader.BitmapDataSize = imageHeader.Width * imageHeader.BPP / 8; imageHeader.BitmapDataSize = (imageHeader.BitmapDataSize + 3) & ~3; imageHeader.BitmapDataSize *= imageHeader.Height; // file size is data size plus offset to data imageHeader.FileSize = imageHeader.BitmapDataOffset + imageHeader.BitmapDataSize; // bitmaps are stored upside down and padded so we always do this void (*CColorConverter_convertFORMATtoFORMAT)(const void*, s32, void*) = 0; switch(image->getColorFormat()) { case ECF_R8G8B8: CColorConverter_convertFORMATtoFORMAT = CColorConverter::convert_R8G8B8toR8G8B8; break; case ECF_A8R8G8B8: CColorConverter_convertFORMATtoFORMAT = CColorConverter::convert_A8R8G8B8toB8G8R8; break; case ECF_A1R5G5B5: CColorConverter_convertFORMATtoFORMAT = CColorConverter::convert_A1R5G5B5toR8G8B8; break; case ECF_R5G6B5: CColorConverter_convertFORMATtoFORMAT = CColorConverter::convert_R5G6B5toR8G8B8; break; } // couldn't find a color converter if (!CColorConverter_convertFORMATtoFORMAT) return false; // write the bitmap header if (file->write(&imageHeader, sizeof(imageHeader)) != sizeof(imageHeader)) return false; u8* scan_lines = (u8*)image->lock(); if (!scan_lines) return false; // size of one pixel in bytes u32 pixel_size = image->getBytesPerPixel(); // length of one row of the source image in bytes u32 row_stride = (pixel_size * imageHeader.Width); // length of one row in bytes, rounded up to nearest 4-byte boundary s32 row_size = ((3 * imageHeader.Width) + 3) & ~3; // allocate and clear memory for our scan line u8* row_pointer = new u8[row_size]; memset(row_pointer, 0, row_size); // convert the image to 24-bit BGR and flip it over s32 y; for (y = imageHeader.Height - 1; 0 <= y; --y) { if (image->getColorFormat()==ECF_R8G8B8) CColorConverter::convert24BitTo24Bit(&scan_lines[y * row_stride], row_pointer, imageHeader.Width, 1, 0, false, true); else // source, length [pixels], destination CColorConverter_convertFORMATtoFORMAT(&scan_lines[y * row_stride], imageHeader.Width, row_pointer); if (file->write(row_pointer, row_size) < row_size) break; } // clean up our scratch area delete [] row_pointer; // give back image handle image->unlock(); return y < 0; }
bool CImageWriterBMP::writeAsset(io::IWriteFile* _file, const SAssetWriteParams& _params, IAssetWriterOverride* _override) { if (!_override) getDefaultOverride(_override); SAssetWriteContext ctx{_params, _file}; // we always write 24-bit color because nothing really reads 32-bit const asset::CImageData* image = # ifndef _DEBUG static_cast<const asset::CImageData*>(_params.rootAsset); # else dynamic_cast<const asset::CImageData*>(_params.rootAsset); # endif assert(image); io::IWriteFile* file = _override->getOutputFile(_file, ctx, {image, 0u}); SBMPHeader imageHeader; imageHeader.Id = 0x4d42; imageHeader.Reserved = 0; imageHeader.BitmapDataOffset = sizeof(imageHeader); imageHeader.BitmapHeaderSize = 0x28; imageHeader.Width = image->getSize().X; imageHeader.Height = image->getSize().Y; imageHeader.Planes = 1; imageHeader.BPP = 24; imageHeader.Compression = 0; imageHeader.PixelPerMeterX = 0; imageHeader.PixelPerMeterY = 0; imageHeader.Colors = 0; imageHeader.ImportantColors = 0; // data size is rounded up to next larger 4 bytes boundary imageHeader.BitmapDataSize = imageHeader.Width * imageHeader.BPP / 8; imageHeader.BitmapDataSize = (imageHeader.BitmapDataSize + 3) & ~3; imageHeader.BitmapDataSize *= imageHeader.Height; // file size is data size plus offset to data imageHeader.FileSize = imageHeader.BitmapDataOffset + imageHeader.BitmapDataSize; // bitmaps are stored upside down and padded so we always do this void (*CColorConverter_convertFORMATtoFORMAT)(const void*, int32_t, void*) = 0; switch(image->getColorFormat()) { case asset::EF_R8G8B8_UNORM: CColorConverter_convertFORMATtoFORMAT = video::CColorConverter::convert_R8G8B8toR8G8B8; break; case asset::EF_B8G8R8A8_UNORM: CColorConverter_convertFORMATtoFORMAT = video::CColorConverter::convert_A8R8G8B8toB8G8R8; break; case asset::EF_A1R5G5B5_UNORM_PACK16: CColorConverter_convertFORMATtoFORMAT = video::CColorConverter::convert_A1R5G5B5toR8G8B8; break; case asset::EF_B5G6R5_UNORM_PACK16: CColorConverter_convertFORMATtoFORMAT = video::CColorConverter::convert_R5G6B5toR8G8B8; break; #ifndef _DEBUG default: break; #endif } // couldn't find a color converter if (!CColorConverter_convertFORMATtoFORMAT) return false; // write the bitmap header if (file->write(&imageHeader, sizeof(imageHeader)) != sizeof(imageHeader)) return false; uint8_t* scan_lines = (uint8_t*)image->getData(); if (!scan_lines) return false; // size of one pixel in bits uint32_t pixel_size_bits = image->getBitsPerPixel(); // length of one row of the source image in bytes uint32_t row_stride = (pixel_size_bits * imageHeader.Width)/8; // length of one row in bytes, rounded up to nearest 4-byte boundary int32_t row_size = ((3 * imageHeader.Width) + 3) & ~3; // allocate and clear memory for our scan line uint8_t* row_pointer = new uint8_t[row_size]; memset(row_pointer, 0, row_size); // convert the image to 24-bit BGR and flip it over int32_t y; for (y = imageHeader.Height - 1; 0 <= y; --y) { if (image->getColorFormat()==asset::EF_R8G8B8_UNORM) video::CColorConverter::convert24BitTo24Bit(&scan_lines[y * row_stride], row_pointer, imageHeader.Width, 1, 0, false, true); else // source, length [pixels], destination CColorConverter_convertFORMATtoFORMAT(&scan_lines[y * row_stride], imageHeader.Width, row_pointer); if (file->write(row_pointer, row_size) < row_size) break; } // clean up our scratch area delete [] row_pointer; return y < 0; }
bool CImageWriterTGA::writeImage(io::IWriteFile *file, IImage *image,u32 param) { STGAHeader imageHeader; imageHeader.IdLength = 0; imageHeader.ColorMapType = 0; imageHeader.ImageType = 2; imageHeader.FirstEntryIndex[0] = 0; imageHeader.FirstEntryIndex[1] = 0; imageHeader.ColorMapLength = 0; imageHeader.ColorMapEntrySize = 0; imageHeader.XOrigin[0] = 0; imageHeader.XOrigin[1] = 0; imageHeader.YOrigin[0] = 0; imageHeader.YOrigin[1] = 0; imageHeader.ImageWidth = image->getDimension().Width; imageHeader.ImageHeight = image->getDimension().Height; // top left of image is the top. the image loader needs to // be fixed to only swap/flip imageHeader.ImageDescriptor = (1 << 5); // chances are good we'll need to swizzle data, so i'm going // to convert and write one scan line at a time. it's also // a bit cleaner this way void (*CColorConverter_convertFORMATtoFORMAT)(const void*, s32, void*) = 0; switch(image->getColorFormat()) { case ECF_A8R8G8B8: CColorConverter_convertFORMATtoFORMAT = CColorConverter::convert_A8R8G8B8toA8R8G8B8; imageHeader.PixelDepth = 32; imageHeader.ImageDescriptor |= 8; break; case ECF_A1R5G5B5: CColorConverter_convertFORMATtoFORMAT = CColorConverter::convert_A1R5G5B5toA1R5G5B5; imageHeader.PixelDepth = 16; imageHeader.ImageDescriptor |= 1; break; case ECF_R5G6B5: CColorConverter_convertFORMATtoFORMAT = CColorConverter::convert_R5G6B5toA1R5G5B5; imageHeader.PixelDepth = 16; imageHeader.ImageDescriptor |= 1; break; case ECF_R8G8B8: CColorConverter_convertFORMATtoFORMAT = CColorConverter::convert_R8G8B8toR8G8B8; imageHeader.PixelDepth = 24; imageHeader.ImageDescriptor |= 0; break; } // couldn't find a color converter if (!CColorConverter_convertFORMATtoFORMAT) return false; if (file->write(&imageHeader, sizeof(imageHeader)) != sizeof(imageHeader)) return false; u8* scan_lines = (u8*)image->lock(); if (!scan_lines) return false; // size of one pixel in bytes u32 pixel_size = image->getBytesPerPixel(); // length of one row of the source image in bytes u32 row_stride = (pixel_size * imageHeader.ImageWidth); // length of one output row in bytes s32 row_size = ((imageHeader.PixelDepth / 8) * imageHeader.ImageWidth); // allocate a row do translate data into u8* row_pointer = new u8[row_size]; u32 y; for (y = 0; y < imageHeader.ImageHeight; ++y) { // source, length [pixels], destination if (image->getColorFormat()==ECF_R8G8B8) CColorConverter::convert24BitTo24Bit(&scan_lines[y * row_stride], row_pointer, imageHeader.ImageWidth, 1, 0, 0, true); else CColorConverter_convertFORMATtoFORMAT(&scan_lines[y * row_stride], imageHeader.ImageWidth, row_pointer); if (file->write(row_pointer, row_size) != row_size) break; } delete [] row_pointer; image->unlock(); STGAFooter imageFooter; imageFooter.ExtensionOffset = 0; imageFooter.DeveloperOffset = 0; strncpy(imageFooter.Signature, "TRUEVISION-XFILE.", 18); if (file->write(&imageFooter, sizeof(imageFooter)) < (s32)sizeof(imageFooter)) return false; return imageHeader.ImageHeight < y; }
//! записывает битмап в файл bool CImageSaverBMP::saveImage(IImage* image, io::IWriteFile* file) { // we always write 24-bit color because nothing really reads 32-bit SBMPHeader imageHeader; imageHeader.Id = 0x4d42; imageHeader.Reserved = 0; imageHeader.BitmapDataOffset = sizeof(imageHeader); imageHeader.BitmapHeaderSize = 0x28; imageHeader.Width = image->getDimension().Width; imageHeader.Height = image->getDimension().Height; imageHeader.Planes = 1; imageHeader.BPP = 24; imageHeader.Compression = 0; imageHeader.PixelPerMeterX = 0; imageHeader.PixelPerMeterY = 0; imageHeader.Colors = 0; imageHeader.ImportantColors = 0; s32 widthInBytes = image->getDimension().Width * 3; s32 lineData = widthInBytes + ((4-(widthInBytes%4)))%4; // data size is rounded up to nearest 4 bytes imageHeader.BitmapDataSize = lineData*image->getDimension().Height + 2; // file size is data size plus offset to data imageHeader.FileSize = imageHeader.BitmapDataOffset + imageHeader.BitmapDataSize; // bitmaps are stored upside down and padded so we always do this void (*CColorConverter_convertFORMATtoFORMAT)(const void*, s32, void*) = 0; switch(image->getColorFormat()) { case ECF_R8G8B8: CColorConverter_convertFORMATtoFORMAT = CColorConverter::convert_R8G8B8toR8G8B8; break; case ECF_A8R8G8B8: CColorConverter_convertFORMATtoFORMAT = CColorConverter::convert_A8R8G8B8toR8G8B8; case ECF_A8B8G8R8: CColorConverter_convertFORMATtoFORMAT = CColorConverter::convert_A8B8G8R8toR8G8B8; break; case ECF_A1R5G5B5: CColorConverter_convertFORMATtoFORMAT = CColorConverter::convert_A1R5G5B5toR8G8B8; break; case ECF_R5G6B5: CColorConverter_convertFORMATtoFORMAT = CColorConverter::convert_R5G6B5toR8G8B8; break; } // couldn't find a color converter if (!CColorConverter_convertFORMATtoFORMAT) return false; // write the bitmap header if (file->write(&imageHeader, sizeof(imageHeader)) != sizeof(imageHeader)) return false; u8* scan_lines = (u8*)image->getData(); if (!scan_lines) return false; // size of one pixel in bytes u32 pixel_size = image->getBytesPerPixel(); // length of one row of the source image in bytes u32 row_stride = (pixel_size * imageHeader.Width); // length of one row in bytes, rounded up to nearest 4-byte boundary s32 row_size = ((3 * imageHeader.Width) + 3) & ~3; // allocate and clear memory for our scan line u8* row_pointer = new u8[row_size]; memset(row_pointer, 0, row_size); u8* flipped_row_pointer = new u8[row_size]; memset(flipped_row_pointer, 0, row_size); // convert the image to 24-bit RGB and flip it over s32 y; for (y = imageHeader.Height - 1; 0 <= y; --y) { // convert the image to 24-bit RGB CColorConverter_convertFORMATtoFORMAT(&scan_lines[y * row_stride], imageHeader.Width, row_pointer); // flip color CColorConverter::convert_R8G8B8toB8G8R8(row_pointer, imageHeader.Width, flipped_row_pointer); if (file->write(flipped_row_pointer, row_size) < (u32)row_size) break; } // final two bytes u16 word=0; file->write(&word, 2); // clean up our scratch area delete [] row_pointer; delete [] flipped_row_pointer; return y < 0; }