Files
opennurbs/opennurbs_bitmap.cpp
Bozo The Builder e7c29061e3 Sync changes from upstream repository
Co-authored-by: Alain <alain@mcneel.com>
Co-authored-by: Andrew Le Bihan <andy@mcneel.com>
Co-authored-by: chuck <chuck@mcneel.com>
Co-authored-by: croudyj <croudyj@gmail.com>
Co-authored-by: Dale Fugier <dale@mcneel.com>
Co-authored-by: Giulio Piacentino <giulio@mcneel.com>
Co-authored-by: Greg Arden <greg@mcneel.com>
Co-authored-by: Jussi Aaltonen <jussi@mcneel.com>
Co-authored-by: kike-garbo <kike@mcneel.com>
Co-authored-by: Steve Baer <steve@mcneel.com>
2022-11-21 14:18:57 -08:00

1357 lines
36 KiB
C++

//
// Copyright (c) 1993-2022 Robert McNeel & Associates. All rights reserved.
// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert
// McNeel & Associates.
//
// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY.
// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF
// MERCHANTABILITY ARE HEREBY DISCLAIMED.
//
// For complete openNURBS copyright information see <http://www.opennurbs.org>.
//
////////////////////////////////////////////////////////////////
#include "opennurbs.h"
#if !defined(ON_COMPILING_OPENNURBS)
// This check is included in all opennurbs source .c and .cpp files to insure
// ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled.
// When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined
// and the opennurbs .h files alter what is declared and how it is declared.
#error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs
#endif
ON_VIRTUAL_OBJECT_IMPLEMENT( ON_Bitmap, ON_Object, "390465E9-3721-11d4-800B-0010830122F0");
ON_OBJECT_IMPLEMENT( ON_WindowsBitmap, ON_Bitmap, "390465EB-3721-11d4-800B-0010830122F0");
ON_OBJECT_IMPLEMENT( ON_EmbeddedBitmap, ON_Bitmap, "772E6FC1-B17B-4fc4-8F54-5FDA511D76D2");
ON_OBJECT_IMPLEMENT( ON_WindowsBitmapEx, ON_WindowsBitmap, "203AFC17-BCC9-44fb-A07B-7F5C31BD5ED9");
const ON_Bitmap* ON_Bitmap::FromModelComponentRef(
const class ON_ModelComponentReference& model_component_reference,
const ON_Bitmap* none_return_value
)
{
const ON_Bitmap* p = ON_Bitmap::Cast(model_component_reference.ModelComponent());
return (nullptr != p) ? p : none_return_value;
}
ON_Bitmap::ON_Bitmap() ON_NOEXCEPT
: ON_ModelComponent(ON_ModelComponent::Type::Image)
{}
ON_Bitmap::ON_Bitmap( const ON_Bitmap& src)
: ON_ModelComponent(ON_ModelComponent::Type::Image,src)
, m_file_reference(src.m_file_reference)
{}
void ON_Bitmap::Dump( ON_TextLog& dump ) const
{
ON_ModelComponent::Dump(dump);
m_file_reference.Dump(dump);
dump.Print("width = %d pixels\n",Width());
dump.Print("height = %d pixels\n",Height());
dump.Print("bits per pixel = %d\n",BitsPerPixel());
dump.Print("size of image = %zu bytes\n",SizeofImage());
}
int ON_Bitmap::Width() const
{
return 0;
}
int ON_Bitmap::Height() const
{
return 0;
}
int ON_Bitmap::BitsPerPixel() const
{
return 0;
}
size_t ON_Bitmap::SizeofScan() const
{
return 0;
}
size_t ON_Bitmap::SizeofImage() const
{
return 0;
}
unsigned char* ON_Bitmap::Bits(
int scan_line_index
)
{
return nullptr;
}
const unsigned char* ON_Bitmap::Bits(
int scan_line_index
) const
{
return nullptr;
}
bool ON_Bitmap::Write(ON_BinaryArchive& archive) const
{
const int major_version = 1;
const int minor_version = 0;
if (!archive.BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK, major_version, minor_version))
return false;
bool rc = false;
for (;;)
{
const unsigned int attributes_filter
= ON_ModelComponent::Attributes::IdAttribute
| ON_ModelComponent::Attributes::IndexAttribute
| ON_ModelComponent::Attributes::NameAttribute
;
ON_ModelComponent::WriteModelComponentAttributes(archive, attributes_filter);
if (!m_file_reference.Write(true, archive))
break;
rc = true;
break;
}
if (!archive.EndWrite3dmChunk())
rc = false;
return rc;
}
bool ON_Bitmap::Read(ON_BinaryArchive& archive)
{
int major_version = 0;
int minor_version = 0;
if (!archive.BeginRead3dmChunk(TCODE_ANONYMOUS_CHUNK, &major_version, &minor_version))
return false;
bool rc = false;
for (;;)
{
if (1 != major_version)
break;
ON_ModelComponent::ReadModelComponentAttributes(archive);
if (!m_file_reference.Read(archive))
break;
rc = true;
break;
}
if (!archive.EndRead3dmChunk())
rc = false;
return rc;
}
const ON_FileReference& ON_Bitmap::FileReference() const
{
return m_file_reference;
}
void ON_Bitmap::SetFileReference(
const ON_FileReference& file_reference
)
{
if ( 0 != ON_FileReference::Compare(m_file_reference,file_reference) )
{
IncrementContentVersionNumber();
m_file_reference = file_reference;
}
}
void ON_Bitmap::SetFileFullPath(
const wchar_t* file_full_path,
bool bSetContentHash
)
{
ON_FileReference file_reference;
file_reference.SetFullPath(file_full_path,bSetContentHash);
SetFileReference(file_reference);
}
static
int ON_WindowsBitmapHelper_PaletteColorCount( int bmiHeader_biClrUsed, int bmiHeader_biBitCount )
{
int color_count = 0;
if ( bmiHeader_biClrUsed )
{
color_count = bmiHeader_biClrUsed;
}
else
{
switch( bmiHeader_biBitCount )
{
case 1:
color_count = 2;
break;
case 4:
color_count = 16;
break;
case 8:
color_count = 256;
break;
default:
color_count = 0;
}
}
return color_count;
}
static
size_t ON_WindowsBitmapHelper_SizeofPalette( int bmiHeader_biClrUsed, int bmiHeader_biBitCount )
{
#if defined(ON_OS_WINDOWS_GDI)
return ( ON_WindowsBitmapHelper_PaletteColorCount(bmiHeader_biClrUsed,bmiHeader_biBitCount) * sizeof(RGBQUAD) );
#else
return ( ON_WindowsBitmapHelper_PaletteColorCount(bmiHeader_biClrUsed,bmiHeader_biBitCount) * sizeof(ON_WindowsRGBQUAD) );
#endif
}
ON_WindowsBitmap::ON_WindowsBitmap( const ON_WindowsBitmap& src )
: ON_Bitmap(src)
{
Internal_Copy(src);
}
ON_WindowsBitmap::~ON_WindowsBitmap()
{
Internal_Destroy();
}
ON_WindowsBitmap& ON_WindowsBitmap::operator=( const ON_WindowsBitmap& src )
{
if ( this != &src )
{
Internal_Destroy();
ON_Bitmap::operator=(src);
Internal_Copy(src);
}
return *this;
}
#if defined(ON_OS_WINDOWS_GDI)
static BITMAPINFO*
ON_WindowsBitmapHelper_AllocBMI(size_t sizeof_palette, size_t sizeof_image)
{
// In theory,
// sz = sizeof(BITMAPINFOHEADER) + sizeof_palette + sizeof_image;
// should work, but BITMAPINFO is only 4 bytes bigger than BITMAPINFOHEADER
// and the allocation below will certainly work.
size_t sz = sizeof(BITMAPINFO) + sizeof_palette + sizeof_image;
BITMAPINFO* bmi = (BITMAPINFO*)onmalloc(sz);
if ( bmi )
{
memset(bmi,0,sizeof(*bmi));
bmi->bmiHeader.biSize = sizeof(bmi->bmiHeader);
}
return bmi;
}
#else
static
ON_WindowsBITMAPINFO* ON_WindowsBitmapHelper_AllocBMI(size_t sizeof_palette, size_t sizeof_image)
{
size_t sz = sizeof(ON_WindowsBITMAPINFO) + sizeof_palette + sizeof_image;
ON_WindowsBITMAPINFO* bmi = (ON_WindowsBITMAPINFO*)onmalloc(sz);
if ( bmi )
{
memset(bmi,0,sizeof(*bmi));
bmi->bmiHeader.biSize = sizeof(bmi->bmiHeader);
}
return bmi;
}
#endif
void ON_WindowsBitmap::Internal_Copy( const ON_WindowsBitmap& src )
{
if ( nullptr != src.m_bmi )
{
const int sizeof_palette = src.SizeofPalette();
const size_t sizeof_image = src.SizeofImage();
m_bmi = ON_WindowsBitmapHelper_AllocBMI( sizeof_palette, sizeof_image );
if ( m_bmi )
{
m_bFreeBMI = 1;
m_bmi->bmiHeader = src.m_bmi->bmiHeader;
if( sizeof_palette > 0 )
{
memcpy(&m_bmi->bmiColors[0],&src.m_bmi->bmiColors[0],sizeof_palette);
}
if ( sizeof_image > 0 )
{
m_bits = (unsigned char*)&m_bmi->bmiColors[PaletteColorCount()];
if ( src.m_bits )
memcpy( m_bits, src.m_bits, sizeof_image );
else
memset( m_bits, 0, sizeof_image );
}
else
m_bits = nullptr;
}
}
}
void ON_WindowsBitmap::Internal_Destroy()
{
if (nullptr != m_bmi)
{
if ( 1 == m_bFreeBMI || 3 == m_bFreeBMI )
onfree(m_bmi);
m_bmi = nullptr;
}
if ( nullptr != m_bits )
{
if ( 2 == m_bFreeBMI || 3 == m_bFreeBMI )
onfree(m_bits);
m_bits = nullptr;
}
m_bFreeBMI = 0;
}
bool ON_WindowsBitmap::IsEmpty() const
{
return ( nullptr == m_bmi || nullptr == m_bits || 0 == Width() || 0 == Height() );
}
bool ON_WindowsBitmap::IsValid( ON_TextLog* text_log ) const
{
bool rc
= ( m_bmi != nullptr && m_bits != nullptr && Width() > 0 && Height() > 0)
? true
: false;
if ( !rc && 0 != text_log )
{
// TODO: add a detailed diagnostic message
text_log->Print("ON_WindowsBitmap is not valid\n");
}
return rc;
}
bool ON_WindowsBitmap::Create(
int width,
int height,
int bits_per_pixel // 1, 2, 4, 8, 16, 24, or 32
)
{
Internal_Destroy();
if ( width < 1 || height < 1 )
{
return false;
}
if ( bits_per_pixel != 1 && bits_per_pixel != 2 && bits_per_pixel != 4
&& bits_per_pixel != 8 && bits_per_pixel != 16 && bits_per_pixel != 24
&& bits_per_pixel != 32 )
{
return false;
}
#if defined(ON_OS_WINDOWS_GDI)
BITMAPINFOHEADER bh;
const DWORD sizeof_RGBQUAD = sizeof(RGBQUAD);
#else
ON_WindowsBITMAPINFOHEADER bh;
const unsigned int sizeof_RGBQUAD = sizeof(ON_WindowsRGBQUAD);
#endif
memset( &bh, 0, sizeof(bh) );
bh.biSize = sizeof(bh);
bh.biWidth = width;
bh.biHeight = height;
bh.biPlanes = 1;
bh.biBitCount = (unsigned short)bits_per_pixel; // cast is safe
bh.biCompression = 0; // BI_RGB
const int sizeof_scan = (((bits_per_pixel*width) + 31) / 32) * 4;
bh.biSizeImage = height*sizeof_scan;
bh.biXPelsPerMeter = 0;
bh.biYPelsPerMeter = 0;
bh.biClrUsed = 0;
bh.biClrImportant = 0;
int palette_color_count = 0;
switch ( bits_per_pixel ) {
case 1:
palette_color_count = 2;
break;
case 4:
palette_color_count = 16;
break;
case 8:
palette_color_count = 256;
break;
}
const int sizeof_palette = palette_color_count*sizeof_RGBQUAD;
m_bmi = ON_WindowsBitmapHelper_AllocBMI( sizeof_palette, bh.biSizeImage );
bool rc = false;
if ( m_bmi /*&& palette_color_count > 0*/)
{
m_bmi->bmiHeader = bh;
m_bits = (unsigned char*)&m_bmi->bmiColors[palette_color_count];
// default palette is gray scale
if ( palette_color_count > 0 )
{
const int rgb_delta = 256/palette_color_count;
int i, rgb;
for ( i = 0, rgb = 0; i < palette_color_count; i++, rgb += rgb_delta )
{
if ( rgb >= 256 ) rgb = 255;
m_bmi->bmiColors[i].rgbBlue = (unsigned char)rgb;
m_bmi->bmiColors[i].rgbGreen = (unsigned char)rgb;
m_bmi->bmiColors[i].rgbRed = (unsigned char)rgb;
m_bmi->bmiColors[i].rgbReserved = 0;
}
}
rc = true;
}
return rc;
}
int ON_WindowsBitmap::Width() const
{
return (m_bmi) ? m_bmi->bmiHeader.biWidth : 0;
}
int ON_WindowsBitmap::Height() const
{
return (m_bmi) ? m_bmi->bmiHeader.biHeight : 0;
}
int ON_WindowsBitmap::PaletteColorCount() const
{
return m_bmi ? ON_WindowsBitmapHelper_PaletteColorCount(m_bmi->bmiHeader.biClrUsed, m_bmi->bmiHeader.biBitCount ) : 0;
}
int ON_WindowsBitmap::SizeofPalette() const
{
return m_bmi ? ((int)ON_WindowsBitmapHelper_SizeofPalette(m_bmi->bmiHeader.biClrUsed, m_bmi->bmiHeader.biBitCount) ) : 0;
}
size_t ON_WindowsBitmap::SizeofScan() const
{
int scan_width = 0;
if ( m_bmi ) {
int bitcount = m_bmi->bmiHeader.biBitCount;
int width = Width();
scan_width = (((bitcount*width) + 31) / 32) * 4;
}
return scan_width;
}
int ON_WindowsBitmap::BitsPerPixel() const
{
return m_bmi ? m_bmi->bmiHeader.biBitCount : 0;
}
size_t ON_WindowsBitmap::SizeofImage() const
{
size_t sizeImage = 0;
if ( nullptr != m_bmi )
{
sizeImage = m_bmi->bmiHeader.biSizeImage;
if (0 == sizeImage)
{
const size_t w = ((((m_bmi->bmiHeader.biWidth * m_bmi->bmiHeader.biBitCount) + 31) & ~31) >> 3);
const size_t h = m_bmi->bmiHeader.biHeight;
sizeImage = w*h;
}
}
return sizeImage;
}
unsigned char* ON_WindowsBitmap::Bits( int scan_index )
{
const size_t sizeof_scan = SizeofScan();
unsigned char* bits = m_bmi ? (unsigned char*)&m_bmi->bmiColors[PaletteColorCount()] : 0;
if ( bits && sizeof_scan && scan_index >= 0 && scan_index < Height() ) {
bits += ( sizeof_scan*scan_index );
}
else {
bits = 0;
}
return bits;
}
const unsigned char* ON_WindowsBitmap::Bits(int scan_index) const
{
const size_t sizeof_scan = SizeofScan();
const unsigned char* bits = m_bmi ? (const unsigned char*)&m_bmi->bmiColors[PaletteColorCount()] : 0;
if ( bits && sizeof_scan && scan_index >= 0 && scan_index < Height() ) {
bits += (sizeof_scan*scan_index);
}
else {
bits = 0;
}
return bits;
}
ON_Color ON_WindowsBitmap::Pixel( int column_index, int row_index ) const
{
return Pixel( column_index, Bits(row_index) );
}
ON_Color ON_WindowsBitmap::Pixel( int column_index, const unsigned char* scanbits ) const
{
int r=0,g=0,b=0,a=0;
unsigned int palindex;
if ( m_bmi && column_index >= 0 && column_index <= Width() && scanbits && !m_bmi->bmiHeader.biCompression ) {
switch( m_bmi->bmiHeader.biBitCount ) {
case 32:
scanbits += (column_index*4);
b = *scanbits++;
g = *scanbits++;
r = *scanbits++;
a = *scanbits;
break;
case 24:
scanbits += (column_index*3);
b = *scanbits++;
g = *scanbits++;
r = *scanbits;
break;
case 8:
// 256 color bitmap uses palette
palindex = scanbits[column_index];
b = m_bmi->bmiColors[palindex].rgbBlue;
g = m_bmi->bmiColors[palindex].rgbGreen;
r = m_bmi->bmiColors[palindex].rgbRed;
a = m_bmi->bmiColors[palindex].rgbReserved;
break;
case 4:
{
// 16 color bitmap uses palette
palindex = scanbits[column_index/2];
if ( !(column_index%2) )
palindex >>= 4;
palindex &= 0x0F;
b = m_bmi->bmiColors[palindex].rgbBlue;
g = m_bmi->bmiColors[palindex].rgbGreen;
r = m_bmi->bmiColors[palindex].rgbRed;
a = m_bmi->bmiColors[palindex].rgbReserved;
}
break;
case 1:
// monochrome bitmap has 2 color palette
palindex = ( scanbits[column_index/8] >> (7-(column_index%8)) ) & 0x01;
b = m_bmi->bmiColors[palindex].rgbBlue;
g = m_bmi->bmiColors[palindex].rgbGreen;
r = m_bmi->bmiColors[palindex].rgbRed;
a = m_bmi->bmiColors[palindex].rgbReserved;
break;
}
}
return ON_Color(r,g,b,a);
}
bool ON_WindowsBitmap::WriteUncompressed( ON_BinaryArchive& file ) const
{
#if defined(ON_OS_WINDOWS_GDI)
BITMAPINFOHEADER bmiHeader;
#else
ON_WindowsBITMAPINFOHEADER bmiHeader;
#endif
if ( m_bmi )
{
bmiHeader = m_bmi->bmiHeader;
bmiHeader.biSize = sizeof(bmiHeader);
}
else
{
memset(&bmiHeader,0,sizeof(bmiHeader));
}
int i;
short s;
i = bmiHeader.biSize;
bool rc = file.WriteInt(i);
i = bmiHeader.biWidth;
if (rc) rc = file.WriteInt(i);
i = bmiHeader.biHeight;
if (rc) rc = file.WriteInt(i);
s = bmiHeader.biPlanes;
if (rc) rc = file.WriteShort(s);
s = bmiHeader.biBitCount;
if (rc) rc = file.WriteShort(s);
i = bmiHeader.biCompression;
if (rc) rc = file.WriteInt(i);
i = bmiHeader.biSizeImage;
if (rc) rc = file.WriteInt(i);
i = bmiHeader.biXPelsPerMeter;
if (rc) rc = file.WriteInt(i);
i = bmiHeader.biYPelsPerMeter;
if (rc) rc = file.WriteInt(i);
i = bmiHeader.biClrUsed;
if (rc) rc = file.WriteInt(i);
i = bmiHeader.biClrImportant;
if (rc) rc = file.WriteInt(i);
if ( rc )
{
const int color_count = PaletteColorCount();
for (i = 0; i < color_count && rc; i++ )
{
if (rc) rc = file.WriteChar( m_bmi->bmiColors[i].rgbBlue );
if (rc) rc = file.WriteChar( m_bmi->bmiColors[i].rgbGreen );
if (rc) rc = file.WriteChar( m_bmi->bmiColors[i].rgbRed );
if (rc) rc = file.WriteChar( m_bmi->bmiColors[i].rgbReserved );
}
const size_t sizeof_image = SizeofImage();
if ( sizeof_image > 0 && rc )
{
if (rc) rc = file.WriteByte( sizeof_image, &m_bmi->bmiColors[color_count] );
}
}
return rc;
}
bool ON_WindowsBitmap::ReadUncompressed( ON_BinaryArchive& file )
{
#if defined(ON_OS_WINDOWS_GDI)
BITMAPINFOHEADER bmiHeader;
#else
ON_WindowsBITMAPINFOHEADER bmiHeader;
#endif
memset(&bmiHeader,0,sizeof(bmiHeader));
Internal_Destroy();
int i;
short s;
bool rc;
for(;;)
{
i = 0;
s = 0;
rc = file.ReadInt(&i); if (!rc) break; bmiHeader.biSize = i;
rc = file.ReadInt(&i); if (!rc) break; bmiHeader.biWidth = i;
rc = file.ReadInt(&i); if (!rc) break; bmiHeader.biHeight = i;
rc = file.ReadShort(&s); if (!rc) break; bmiHeader.biPlanes = s;
rc = file.ReadShort(&s); if (!rc) break; bmiHeader.biBitCount = s;
rc = file.ReadInt(&i); if (!rc) break; bmiHeader.biCompression = i;
rc = file.ReadInt(&i); if (!rc) break; bmiHeader.biSizeImage = i;
rc = file.ReadInt(&i); if (!rc) break; bmiHeader.biXPelsPerMeter = i;
rc = file.ReadInt(&i); if (!rc) break; bmiHeader.biYPelsPerMeter = i;
rc = file.ReadInt(&i); if (!rc) break; bmiHeader.biClrUsed = i;
rc = file.ReadInt(&i); if (!rc) break; bmiHeader.biClrImportant = i;
break;
}
if ( rc )
{
bmiHeader.biSize = sizeof(bmiHeader);
const size_t sizeof_palette = ON_WindowsBitmapHelper_SizeofPalette(bmiHeader.biClrUsed, bmiHeader.biBitCount );
const size_t sizeof_image = bmiHeader.biSizeImage;
m_bmi = ON_WindowsBitmapHelper_AllocBMI( sizeof_palette, sizeof_image );
if ( !m_bmi )
{
rc = false;
}
else
{
m_bFreeBMI = 1;
m_bmi->bmiHeader = bmiHeader;
const int color_count = ON_WindowsBitmapHelper_PaletteColorCount(bmiHeader.biClrUsed, bmiHeader.biBitCount );
for (int i_for_loop = 0; i_for_loop < color_count && rc; i_for_loop++ )
{
if (rc) rc = file.ReadChar( &m_bmi->bmiColors[i_for_loop].rgbBlue );
if (rc) rc = file.ReadChar( &m_bmi->bmiColors[i_for_loop].rgbGreen );
if (rc) rc = file.ReadChar( &m_bmi->bmiColors[i_for_loop].rgbRed );
if (rc) rc = file.ReadChar( &m_bmi->bmiColors[i_for_loop].rgbReserved );
}
if ( sizeof_image > 0 && rc )
{
m_bits = (unsigned char*)&m_bmi->bmiColors[color_count];
if (rc) rc = file.ReadByte( sizeof_image, m_bits );
}
}
}
return rc;
}
bool ON_WindowsBitmap::Write( ON_BinaryArchive& archive ) const
{
return Internal_WriteV5(archive);
}
bool ON_WindowsBitmap::Internal_WriteV5( ON_BinaryArchive& archive ) const
{
return WriteCompressed(archive);
}
bool ON_WindowsBitmap::Read( ON_BinaryArchive& archive )
{
return Internal_ReadV5(archive);
}
bool ON_WindowsBitmap::Internal_ReadV5( ON_BinaryArchive& file )
{
bool rc = false;
if ( file.Archive3dmVersion() == 1 )
rc = ReadUncompressed(file);
else
rc = ReadCompressed(file);
return rc;
}
bool ON_WindowsBitmapEx::Write(ON_BinaryArchive& archive) const
{
return Internal_WriteV5(archive);
}
bool ON_WindowsBitmapEx::Read(ON_BinaryArchive& archive)
{
return Internal_ReadV5(archive);
}
bool ON_WindowsBitmapEx::Internal_WriteV5( ON_BinaryArchive& file ) const
{
bool rc = file.Write3dmChunkVersion(1,0);
if (rc)
rc = file.WriteString(FileReference().FullPath());
if (rc)
rc = ON_WindowsBitmap::WriteCompressed(file);
return rc;
}
bool ON_WindowsBitmapEx::Internal_ReadV5( ON_BinaryArchive& file )
{
Internal_Destroy();
SetFileReference(ON_FileReference::Unset);
int major_version = 0;
int minor_version = 0;
bool rc = file.Read3dmChunkVersion(&major_version,&minor_version);
if (rc && 1 == major_version )
{
// Calling ON_WindowsBitmap::ReadCompressed() destroys
// m_bitmap_filename, so we have to read it into a local
// string and make the assigment after calling
// ON_WindowsBitmap::ReadCompressed().
ON_wString bitmap_filename;
if (rc)
rc = file.ReadString(bitmap_filename);
if ( !rc)
bitmap_filename.Destroy();
if (rc)
rc = ON_WindowsBitmap::ReadCompressed(file);
bitmap_filename.TrimLeftAndRight();
if (bitmap_filename.IsNotEmpty())
{
ON_FileReference file_reference;
if (ON_FileSystemPath::IsRelativePath(bitmap_filename))
file_reference.SetRelativePath(bitmap_filename);
else
file_reference.SetFullPath(bitmap_filename,false);
SetFileReference(file_reference);
}
}
else
rc = false;
return rc;
}
bool ON_WindowsBitmap::WriteCompressed( ON_BinaryArchive& file ) const
{
int color_count = 0;
int sizeof_palette = 0;
size_t sizeof_image = 0;
bool bContiguousBitmap = IsContiguous();
#if defined(ON_OS_WINDOWS_GDI)
BITMAPINFOHEADER bmiHeader;
#else
ON_WindowsBITMAPINFOHEADER bmiHeader;
#endif
if ( m_bmi )
{
bmiHeader = m_bmi->bmiHeader;
color_count = PaletteColorCount();
sizeof_palette = color_count*sizeof(*m_bmi->bmiColors);
sizeof_image = SizeofImage();
if ( 0 == sizeof_image )
bContiguousBitmap = true;
}
else
{
bContiguousBitmap = true;
color_count = 0;
sizeof_palette = 0;
sizeof_image = 0;
memset(&bmiHeader,0,sizeof(bmiHeader));
}
int i;
short s;
i = bmiHeader.biSize;
bool rc = file.WriteInt(i);
i = bmiHeader.biWidth;
if (rc) rc = file.WriteInt(i);
i = bmiHeader.biHeight;
if (rc) rc = file.WriteInt(i);
s = bmiHeader.biPlanes;
if (rc) rc = file.WriteShort(s);
s = bmiHeader.biBitCount;
if (rc) rc = file.WriteShort(s);
i = bmiHeader.biCompression;
if (rc) rc = file.WriteInt(i);
i = bmiHeader.biSizeImage;
if (rc) rc = file.WriteInt(i);
i = bmiHeader.biXPelsPerMeter;
if (rc) rc = file.WriteInt(i);
i = bmiHeader.biYPelsPerMeter;
if (rc) rc = file.WriteInt(i);
i = bmiHeader.biClrUsed;
if (rc) rc = file.WriteInt(i);
i = bmiHeader.biClrImportant;
if (rc) rc = file.WriteInt(i);
if ( rc )
{
if ( bContiguousBitmap )
{
const size_t sizeof_buffer = sizeof_palette + sizeof_image;
// palette and bits are compressed in a single chunk
rc = file.WriteCompressedBuffer( sizeof_buffer, (0 != m_bmi) ? m_bmi->bmiColors : 0);
}
else
{
// 28 July 2003
// Added support for writing non-contiguous bitmaps
// palette
rc = file.WriteCompressedBuffer( sizeof_palette, m_bmi->bmiColors );
if (rc)
{
// image bits
rc = file.WriteCompressedBuffer( sizeof_image, m_bits );
}
}
}
return rc;
}
bool ON_WindowsBitmap::ReadCompressed( ON_BinaryArchive& file )
{
Internal_Destroy();
bool bFailedCRC = false;
#if defined(ON_OS_WINDOWS_GDI)
BITMAPINFOHEADER bmiHeader;
#else
ON_WindowsBITMAPINFOHEADER bmiHeader;
#endif
memset(&bmiHeader,0,sizeof(bmiHeader));
int i;
short s;
bool rc;
for(;;)
{
i = 0;
s = 0;
rc = file.ReadInt(&i); if (!rc) break; bmiHeader.biSize = i;
rc = file.ReadInt(&i); if (!rc) break; bmiHeader.biWidth = i;
rc = file.ReadInt(&i); if (!rc) break; bmiHeader.biHeight = i;
rc = file.ReadShort(&s); if (!rc) break; bmiHeader.biPlanes = s;
rc = file.ReadShort(&s); if (!rc) break; bmiHeader.biBitCount = s;
rc = file.ReadInt(&i); if (!rc) break; bmiHeader.biCompression = i;
rc = file.ReadInt(&i); if (!rc) break; bmiHeader.biSizeImage = i;
rc = file.ReadInt(&i); if (!rc) break; bmiHeader.biXPelsPerMeter = i;
rc = file.ReadInt(&i); if (!rc) break; bmiHeader.biYPelsPerMeter = i;
rc = file.ReadInt(&i); if (!rc) break; bmiHeader.biClrUsed = i;
rc = file.ReadInt(&i); if (!rc) break; bmiHeader.biClrImportant = i;
break;
}
if ( rc )
{
bmiHeader.biSize = sizeof(bmiHeader);
const size_t sizeof_palette = ON_WindowsBitmapHelper_SizeofPalette(bmiHeader.biClrUsed, bmiHeader.biBitCount );
const size_t sizeof_image = bmiHeader.biSizeImage;
m_bmi = ON_WindowsBitmapHelper_AllocBMI(sizeof_palette,sizeof_image);
if ( !m_bmi )
{
rc = false;
}
else
{
m_bFreeBMI = 1;
m_bmi->bmiHeader = bmiHeader;
m_bmi->bmiHeader.biSize = sizeof(m_bmi->bmiHeader);
const int color_count = ON_WindowsBitmapHelper_PaletteColorCount(bmiHeader.biClrUsed, bmiHeader.biBitCount );
if ( sizeof_image > 0 )
m_bits = (unsigned char*)&m_bmi->bmiColors[color_count];
size_t sizeof_buffer = 0;
rc = file.ReadCompressedBufferSize( &sizeof_buffer );
if (rc)
{
const size_t sizeof_colors = color_count*sizeof(*m_bmi->bmiColors);
if ( sizeof_buffer == sizeof_colors
|| sizeof_buffer == sizeof_colors + sizeof_image
)
{
// palette and image bits are compressed into one or two chunks
rc = file.ReadCompressedBuffer( sizeof_buffer, m_bmi->bmiColors, &bFailedCRC );
if ( rc && sizeof_image > 0 && sizeof_buffer == sizeof_colors )
{
// 28 July 2003
// Added support for reading non-contiguous bitmaps
sizeof_buffer = 0;
rc = file.ReadCompressedBufferSize( &sizeof_buffer );
if (rc)
{
if ( sizeof_buffer == sizeof_image )
{
// image bits are compressed into a separatechunk
rc = file.ReadCompressedBuffer( sizeof_buffer, &m_bmi->bmiColors[color_count], &bFailedCRC );
}
else
{
ON_ERROR("ON_WindowsBitmap::ReadCompressed() image bits buffer size mismatch\n");
rc = false;
}
}
}
}
else
{
ON_ERROR("ON_WindowsBitmap::ReadCompressed() buffer size mismatch\n");
rc = false;
}
}
}
}
return rc;
}
bool ON_WindowsBitmap::IsContiguous() const
{
bool rc = false;
if ( 0 != m_bmi && 0 != m_bits && m_bmi->bmiHeader.biSizeImage > 0 )
{
// p1 points to the first byte after the color palette.
unsigned char* p1 = (unsigned char*)&m_bmi->bmiColors[PaletteColorCount()];
rc = ( m_bits == p1 );
}
return rc;
}
#if defined(ON_OS_WINDOWS_GDI)
#pragma message( " --- OpenNURBS including Windows BITMAPINFO support in ON_WindowsBitmap" )
ON_WindowsBitmap::ON_WindowsBitmap( const BITMAPINFO& src )
: m_bmi(0), m_bits(0), m_bFreeBMI(0)
{
*this = src;
}
ON_WindowsBitmap& ON_WindowsBitmap::operator=( const BITMAPINFO& src )
{
Internal_Destroy();
ON_Bitmap::operator=(ON_Bitmap::Unset);
int color_count = ON_WindowsBitmapHelper_PaletteColorCount(src.bmiHeader.biClrUsed, src.bmiHeader.biBitCount );
Create(&src,(const unsigned char*)(&src.bmiColors[color_count]),true);
return *this;
}
ON_WindowsBitmap::ON_WindowsBitmap( const BITMAPINFO* src )
{
if ( 0 != src )
{
int color_count = ON_WindowsBitmapHelper_PaletteColorCount(src->bmiHeader.biClrUsed, src->bmiHeader.biBitCount );
Create(src,(const unsigned char*)(&src->bmiColors[color_count]),false);
}
}
#endif
#if defined ON_OS_WINDOWS_GDI
bool ON_WindowsBitmap::Create( const BITMAPINFO* bmi, const unsigned char* bits, bool bCopy )
#else
bool ON_WindowsBitmap::Create( const ON_WindowsBITMAPINFO* bmi, const unsigned char* bits, bool bCopy )
#endif
{
Internal_Destroy();
bool rc = false;
m_bFreeBMI = 0;
m_bmi = 0;
m_bits = 0;
if ( 0 != bmi )
{
if ( bCopy )
{
// allocate a contiguous Windows device independent bitmap
const size_t sizeof_palette = ON_WindowsBitmapHelper_SizeofPalette(bmi->bmiHeader.biClrUsed, bmi->bmiHeader.biBitCount );
const int sizeof_image = bmi->bmiHeader.biSizeImage;
m_bmi = ON_WindowsBitmapHelper_AllocBMI( sizeof_palette, (bCopy?sizeof_image:0) );
if ( 0 != m_bmi )
{
rc = true;
m_bFreeBMI = 1; // ~ON_WindowsBitmap will free the m_bmi pointer
m_bmi->bmiHeader = bmi->bmiHeader;
m_bmi->bmiHeader.biSize = sizeof(m_bmi->bmiHeader);
int color_count = ON_WindowsBitmapHelper_PaletteColorCount(bmi->bmiHeader.biClrUsed, bmi->bmiHeader.biBitCount );
if ( color_count > 0 )
{
memcpy( &m_bmi->bmiColors[0], &bmi->bmiColors[0], color_count*sizeof(m_bmi->bmiColors[0]) );
}
if ( bCopy && sizeof_image > 0 )
{
m_bits = (unsigned char*)(&m_bmi->bmiColors[color_count]);
if ( 0 != bits )
memcpy( m_bits, bits, sizeof_image );
else
memset( m_bits, 0, sizeof_image );
}
}
}
else
{
// share BITMAPINFO memory
rc = true;
#if defined ON_OS_WINDOWS_GDI
m_bmi = const_cast<BITMAPINFO*>(bmi);
#else
m_bmi = const_cast<ON_WindowsBITMAPINFO*>(bmi);
#endif
m_bits = const_cast<unsigned char*>(bits);
}
}
return rc;
}
#if !defined(ON_OS_WINDOWS_GDI)
bool ON_WindowsBitmap::Create(const struct ON_WindowsBITMAPINFO* src)
{
bool rc = false;
*this = ON_WindowsBitmap::Unset;
m_bFreeBMI = 0;
m_bmi = 0;
m_bits = 0;
if ( 0 != src )
{
rc = true;
int color_count = ON_WindowsBitmapHelper_PaletteColorCount(src->bmiHeader.biClrUsed, src->bmiHeader.biBitCount );
m_bmi = (struct ON_WindowsBITMAPINFO*)src;
m_bits = (unsigned char*)(&src->bmiColors[color_count]);
}
return rc;
}
#endif
////////////////////////////////////////////////////////////////////////////////
//
// ON_EmbeddedBitmap - used to embed bitmaps in 3dm archives
//
ON_EmbeddedBitmap::~ON_EmbeddedBitmap()
{
Internal_Destroy();
}
ON_EmbeddedBitmap::ON_EmbeddedBitmap(const ON_EmbeddedBitmap& src)
: ON_Bitmap(src)
{
Internal_Copy(src);
}
ON_EmbeddedBitmap& ON_EmbeddedBitmap::operator=(const ON_EmbeddedBitmap& src)
{
if (this != &src)
{
Internal_Destroy();
ON_Bitmap::operator=(src);
Internal_Copy(src);
}
return *this;
}
void ON_EmbeddedBitmap::Internal_Copy(
const ON_EmbeddedBitmap& src
)
{
if (nullptr != src.m_buffer && src.m_sizeof_buffer > 0)
{
Create(src.m_sizeof_buffer);
if (nullptr != m_buffer && m_sizeof_buffer == src.m_sizeof_buffer)
{
memcpy(const_cast<void*>(m_buffer), src.m_buffer, m_sizeof_buffer);
m_buffer_crc32 = src.m_buffer_crc32;
}
}
}
void ON_EmbeddedBitmap::Internal_Destroy()
{
void* p
= (m_managed_buffer && m_sizeof_buffer > 0)
? const_cast<void*>(m_buffer)
: nullptr;
m_buffer = nullptr;
m_sizeof_buffer = 0;
m_managed_buffer = false;
m_buffer_crc32 = 0;
if (nullptr != p)
onfree(p);
}
void ON_EmbeddedBitmap::Create( size_t sizeof_buffer )
{
Internal_Destroy();
if ( sizeof_buffer > 0 )
{
m_buffer = onmalloc(sizeof_buffer);
if ( 0 != m_buffer )
{
m_sizeof_buffer = sizeof_buffer;
m_managed_buffer = true;
}
}
}
bool ON_EmbeddedBitmap::IsValid( ON_TextLog* text_log ) const
{
if ( 0 == m_buffer )
{
if ( 0 != text_log )
text_log->Print("ON_EmbeddedBitmap m_buffer = 0\n");
return false;
}
return true;
}
bool ON_EmbeddedBitmap::Write(ON_BinaryArchive& archive) const
{
return Internal_WriteV5(archive);
}
bool ON_EmbeddedBitmap::Internal_WriteV5( ON_BinaryArchive& file ) const
{
bool rc = false;
for (;;)
{
if (!file.Write3dmChunkVersion(1, 1))
break;
if (!file.WriteString( FileReference().FullPath() ) )
break;
if (!file.WriteInt(m_buffer_crc32))
break;
const int i = 1; // 1 = compressed
if (!file.WriteInt(i))
break;
if (!file.WriteCompressedBuffer(m_sizeof_buffer, m_buffer))
break;
// version 1.1 added id and name
if (!file.WriteUuid(Id()))
break;
if (!file.WriteString(Name()))
break;
rc = true;
break;
}
return rc;
}
bool ON_EmbeddedBitmap::Read(ON_BinaryArchive& archive)
{
return Internal_ReadV5(archive);
}
bool ON_EmbeddedBitmap::Internal_ReadV5( ON_BinaryArchive& file )
{
Internal_Destroy();
bool rc = false;
for (;;)
{
int major_version = 0;
int minor_version = 0;
if (!file.Read3dmChunkVersion(&major_version, &minor_version))
break;
if (1 != major_version)
break;
ON_wString file_name;
if (!file.ReadString(file_name))
break;
ON_FileReference file_reference;
if (ON_FileSystemPath::IsRelativePath(file_name))
file_reference.SetRelativePath(file_name);
else
file_reference.SetFullPath(file_name, false);
SetFileReference(file_reference);
if (!file.ReadInt(&m_buffer_crc32))
break;
int i = -1;
if (!file.ReadInt(&i))
break;
if (0 == i)
{
// uncompressed
#pragma ON_PRAGMA_WARNING_PUSH
#pragma ON_PRAGMA_WARNING_DISABLE_MSC(4996)
#pragma ON_PRAGMA_WARNING_DISABLE_CLANG("-Wdeprecated-declarations")
if (!file.ReadSize(&m_sizeof_buffer))
break;
#pragma ON_PRAGMA_WARNING_POP
void* buffer = nullptr;
if (m_sizeof_buffer > 0)
{
buffer = onmalloc(m_sizeof_buffer);
m_managed_buffer = true;
}
if (!file.ReadByte(m_sizeof_buffer, buffer))
{
if (nullptr != buffer)
onfree(buffer);
break;
}
m_buffer = buffer;
}
else if (1 == i)
{
// compressed
if (!file.ReadCompressedBufferSize(&m_sizeof_buffer))
break;
void* buffer = nullptr;
if (m_sizeof_buffer > 0)
{
buffer = onmalloc(m_sizeof_buffer);
}
bool bFailedCRC = false;
if (!file.ReadCompressedBuffer(m_sizeof_buffer, buffer, &bFailedCRC))
{
if (nullptr != buffer)
onfree(buffer);
break;
}
m_buffer = buffer;
m_managed_buffer = true;
}
else
{
// invalid
break;
}
if (minor_version < 1)
{
rc = true;
break;
}
// version 1.1 added id and name
ON_UUID bitmap_id = ON_nil_uuid;
if (!file.ReadUuid(bitmap_id))
break;
SetId(bitmap_id);
ON_wString bitmap_name;
if (!file.ReadString(bitmap_name))
break;
SetName(bitmap_name);
rc = true;
break;
}
if (rc && IdIsNil())
SetId(); // older formats had nil ids - modern times require unique component ids.
return rc;
}
size_t ON_EmbeddedBitmap::SizeofImage() const
{
return m_sizeof_buffer;
}
unsigned char* ON_EmbeddedBitmap::Bits(int scan_line_index)
{
return const_cast<unsigned char*>((0 == scan_line_index) ? static_cast<const unsigned char*>(m_buffer) : nullptr);
}
const unsigned char* ON_EmbeddedBitmap::Bits(int scan_line_index) const
{
return (0 == scan_line_index) ? static_cast<const unsigned char*>(m_buffer) : nullptr;
}
unsigned int ON_Bitmap::SizeOf() const
{
const size_t sizeof_baseclass = ON_ModelComponent::SizeOf();
const size_t sizeof_class = sizeof(*this) - sizeof(ON_ModelComponent);
return (unsigned int)(sizeof_baseclass + sizeof_class);
}
unsigned int ON_EmbeddedBitmap::SizeOf() const
{
const size_t sizeof_baseclass = ON_Bitmap::SizeOf();
const size_t sizeof_class = sizeof(*this) - sizeof(ON_Bitmap);
return (unsigned int)(sizeof_baseclass + sizeof_class + m_sizeof_buffer);
}
unsigned int ON_WindowsBitmap::SizeOf() const
{
const size_t sizeof_baseclass = ON_Bitmap::SizeOf();
const size_t sizeof_class = sizeof(*this) - sizeof(ON_Bitmap);
return (unsigned int)(sizeof_baseclass + sizeof_class + SizeofImage());
}