Bitmap/C++: Difference between revisions
< Bitmap
Content added Content deleted
m (moved Basic bitmap storage/C++ to Bitmap/C++) |
m (Fixed syntax highlighting.) |
||
(One intermediate revision by one other user not shown) | |||
Line 1: | Line 1: | ||
{{collection|Basic bitmap storage}} |
{{collection|Basic bitmap storage}} |
||
< |
<syntaxhighlight lang="cpp">#include <cstddef> |
||
#include <stdexcept> |
|||
// CBitmap as found below is a class that represents bitmap images |
|||
// This class does not implement a copy constructor, nor |
|||
// in main memory, alternatively the images could be stored in |
|||
// does it override the = operatior. If an instance of this object |
|||
// memory in a graphics system (OpenGL, DirectX, ...), but this |
|||
// needs to survive outside the scope it was created in, allocate it |
|||
// would not be generic, nor simple. |
|||
// on the heap (e.g. pBitmap = new CBitmap(x,y) ), and pass the pointer |
|||
// around. |
|||
class CBitmap |
class CBitmap |
||
{ |
{ |
||
private: |
|||
// symbolic constants for the red/green/blue indices. The explicit values |
|||
enum { red_index, green_index, blue_index, num_channels }; |
|||
// are given for clarity, because the program relies on them; omitting them |
|||
// would not change the values of the enumerators |
|||
enum { red_index = 0, green_index = 1, blue_index = 2, num_channels = 3 }; |
|||
protected: |
|||
// This pointer will point to the region of memory that will hold the |
|||
// image data. |
|||
char* m_pImageData; |
|||
private: |
|||
// The "pitch" is the amount of memory a single horizontal line of an |
|||
char* m_pImageData; |
|||
// image takes in memory. This is NOT necessarily equal to the width |
|||
unsigned int m_width; |
|||
// of the image, and certainly not in our case, because each pixel |
|||
// takes up three bytes. |
|||
unsigned int m_pitch; |
|||
// The height will correspond to the number of rows in the image. |
|||
unsigned int m_height; |
unsigned int m_height; |
||
public: |
public: |
||
// |
// If allocation fails, "new" will throw a std::bad_alloc exception. |
||
// For automatic variables, the result is that all accesses to the |
|||
// class' members are skipped, by leaving the scope where the object |
|||
// is created, and visible. |
|||
// For dynamic variables, created on the heap, this will not result |
|||
// in a memory leak, because the memory allocated for the CBitmap |
|||
// is freed as a result of the exception in this case. It is the |
|||
// responsability of the client not to access the class' members |
|||
// after such a failed allocation. |
|||
// The result of this is that the m_pImageData member variable |
|||
// will never be 0 in a legal access through the class' member |
|||
// functions, and thus we can be certain of the invariant (m_pImageData != 0). |
|||
CBitmap(unsigned int width, unsigned int height): |
CBitmap(unsigned int width, unsigned int height): |
||
m_pImageData(new char[num_channels * width * height]), |
|||
m_width(width), |
|||
// This exception propagates out of the constructor and causes the |
|||
// object to be disposed of (without even running the destructor). |
|||
// Since this pointer will never again be changed during the lifetime |
|||
// of objects of this class, this means it will never be NULL in any |
|||
// object of this class. Tests for NULL are therefore not needed |
|||
// anywhere in this class. |
|||
m_pImageData(new char[num_channels*width*height]), |
|||
// Since we use num_channels bytes of memory for each pixel, |
|||
// then our pitch is our width * num_channels. |
|||
m_pitch(width * num_channels), |
|||
// Save our height off so we can perform sanity checks |
|||
// when SetPixel() and GetPixel() are called. |
|||
m_height(height) |
m_height(height) |
||
{ |
{ |
||
} |
|||
// The presence of this copy constructor enables pass-by-value, |
|||
// which is strongly discouraged, due to large amount of work |
|||
// involved in copying. Use pass-by-reference to avoid the copy. |
|||
CBitmap(CBitmap const &original): |
|||
m_pImageData(new char[num_channels * original.m_width * original.m_height]), |
|||
m_width(original.m_width), |
|||
m_height(original.m_height) |
|||
{ |
|||
CopyImageDataFrom(original.m_pImageData); |
|||
} |
} |
||
Line 56: | Line 55: | ||
} |
} |
||
public: |
|||
// Using references because handling image data with function calls |
|||
// An assignment operator is defined with copy-semantics. When an |
|||
// on a per-pixel basis is slow enough. No need to make copies of all |
|||
// allocation error occurs, an exception is thrown (which should |
|||
// the arguments. |
|||
// be caught by the client, and the object destructed) and the |
|||
bool SetPixel(const unsigned int& x, const unsigned int& y, char& R, char& G, char& B) |
|||
// original data is preserved, to satisfy the invariant (m_pImageData != 0). |
|||
// post-condition: this bitmap becomes a uniform copy of the original. |
|||
// exception: failed allocation will cause the image data to be unchanged. |
|||
CBitmap& operator=(CBitmap const &original) |
|||
{ |
{ |
||
if ( this == &original ) |
|||
{ |
|||
return *this; |
|||
} |
|||
// where the program happens to have legal access to the |
|||
// calculated memory address, you'll get garbage data at best. |
|||
// At worst, you may be returning values that represent |
|||
// sensitive data. |
|||
if( (y >= m_height) || (x * num_channels >= m_pitch) ) |
|||
// Indicate that we were not successful. |
|||
return false; |
|||
try |
|||
{ |
|||
char *new_image_data = new char[num_channels * original.m_width * original.m_height]; |
|||
// Some optimizing compilers will store off this value in |
|||
delete [] m_pImageData; |
|||
m_pImageData = new_image_data; |
|||
m_width = original.m_width; |
|||
m_height = original.m_height; |
|||
CopyImageDataFrom(original.m_pImageData); |
|||
} |
|||
catch ( ... ) |
|||
{ |
|||
throw std::runtime_error( "assignment failed, original data conserved" ); |
|||
} |
|||
return *this; |
|||
} |
|||
R = m_pImageData[pixel_index + red_index]; |
|||
// The green byte sits at the byte following the red byte. |
|||
G = m_pImageData[pixel_index + green_index]; |
|||
public: |
|||
// The blue byte sits at the byte following the green byte. |
|||
bool SetPixel(unsigned int x, unsigned int y, char R, char G, char B) |
|||
B = m_pImageData[pixel_index + blue_index]; |
|||
{ |
|||
if ( ! IsWithinBitmap(x, y) ) |
|||
{ |
|||
return false; |
|||
} |
|||
unsigned int pixel_index = ImageCoordinateToPixelIndex(x, y); |
|||
SetColorValueAtIndex( pixel_index, R, G, B ); |
|||
// Indicate that we were successful. |
|||
return true; |
return true; |
||
} |
} |
||
bool GetPixel(unsigned int x, unsigned int y, char& R, char& G, char& B) |
|||
// Using references because handling image data with function calls |
|||
// on a per-pixel basis is slow enough already. No need to make copies |
|||
// of all of the arguments. |
|||
bool GetPixel(const unsigned int& x, const unsigned int& y, char& R, char& G, char& B) |
|||
{ |
{ |
||
if ( ! IsWithinBitmap(x, y) ) |
|||
{ |
|||
// dimensions will cause problems. However, when you're writing |
|||
// data, as opposed to reading it, if memory protections fail or |
|||
// aren't available, you can cause memory corruption, which can |
|||
// be a particularly difficult type of bug to track down. |
|||
if( (y >= m_height) || (x * num_channels >= m_pitch) ) |
|||
// Indicate that we were not successful. |
|||
return false; |
return false; |
||
} |
|||
unsigned int pixel_index = ImageCoordinateToPixelIndex(x, y); |
|||
GetColorValueAtIndex( pixel_index, R, G, B ); |
|||
unsigned int pixel_index = (y * m_pitch + x) * num_channels; |
|||
return true; |
|||
} |
|||
R = m_pImageData[pixel_index + red_index]; |
|||
void Fill(char R, char G, char B) |
|||
// The green byte sits at the byte following the red byte. |
|||
{ |
|||
G = m_pImageData[pixel_index + green_index]; |
|||
for(unsigned int pixel_index = 0; |
|||
pixel_index < m_height * m_width * num_channels; |
|||
pixel_index += num_channels) |
|||
SetColorValueAtIndex( pixel_index, R, G, B ); |
|||
} |
|||
private: |
|||
// The blue byte sits at the byte following the green byte. |
|||
// An alternative to status flags is the use of exceptions. |
|||
B = m_pImageData[pixel_index + blue_index]; |
|||
bool IsWithinBitmap(unsigned int x, unsigned int y) |
|||
{ |
|||
return y < m_height && x < m_width; |
|||
} |
|||
unsigned int ImageCoordinateToPixelIndex(unsigned int x, unsigned int y) |
|||
// Indicate that we were successful. |
|||
{ |
|||
return (y * m_width + x) * num_channels; |
|||
} |
} |
||
void SetColorValueAtIndex(unsigned int pixel_index, char R, char G, char B) |
|||
{ |
{ |
||
m_pImageData[pixel_index + red_index] = R; |
|||
// Since we fill the whole image with the same color, we don't care about the actual image dimensions. |
|||
m_pImageData[pixel_index + green_index] = G; |
|||
// All we need to know is that the pixels are densely packed, and where to stop. |
|||
m_pImageData[pixel_index + blue_index] = B; |
|||
for(unsigned int pixel_index = 0; pixel_index < m_height * m_pitch; pixel_index += num_channels) |
|||
} |
|||
m_pImageData[pixel_index + red_index] = R; |
|||
m_pImageData[pixel_index + green_index] = G; |
|||
m_pImageData[pixel_index + blue_index] = B; |
|||
} |
|||
void GetColorValueAtIndex(unsigned int pixel_index, char R, char G, char B) |
|||
return true; |
|||
{ |
|||
R = m_pImageData[pixel_index + red_index]; |
|||
G = m_pImageData[pixel_index + green_index]; |
|||
B = m_pImageData[pixel_index + blue_index]; |
|||
} |
} |
||
private: |
|||
void CopyImageDataFrom(char *source) |
|||
// Prevent the compiler from creating implicit copy constructor and copy assignment |
|||
{ |
|||
// operators. Those defauls would have incorrect semantics for this class. |
|||
// An alternative implementation using memcpy would be more efficient |
|||
CBitmap(CBitmap const&); // not implemented |
|||
// on almost all platforms. |
|||
for (unsigned int i = 0; i < num_channels * m_width * m_height; ++i) |
|||
};</lang> |
|||
m_pImageData[i] = source[i]; |
|||
} |
|||
};</syntaxhighlight> |
Latest revision as of 15:55, 1 September 2022
Bitmap/C++ is part of Basic bitmap storage. You may find other members of Basic bitmap storage at Category:Basic bitmap storage.
#include <cstddef>
#include <stdexcept>
// CBitmap as found below is a class that represents bitmap images
// in main memory, alternatively the images could be stored in
// memory in a graphics system (OpenGL, DirectX, ...), but this
// would not be generic, nor simple.
class CBitmap
{
private:
enum { red_index, green_index, blue_index, num_channels };
private:
char* m_pImageData;
unsigned int m_width;
unsigned int m_height;
public:
// If allocation fails, "new" will throw a std::bad_alloc exception.
// For automatic variables, the result is that all accesses to the
// class' members are skipped, by leaving the scope where the object
// is created, and visible.
// For dynamic variables, created on the heap, this will not result
// in a memory leak, because the memory allocated for the CBitmap
// is freed as a result of the exception in this case. It is the
// responsability of the client not to access the class' members
// after such a failed allocation.
// The result of this is that the m_pImageData member variable
// will never be 0 in a legal access through the class' member
// functions, and thus we can be certain of the invariant (m_pImageData != 0).
CBitmap(unsigned int width, unsigned int height):
m_pImageData(new char[num_channels * width * height]),
m_width(width),
m_height(height)
{
}
// The presence of this copy constructor enables pass-by-value,
// which is strongly discouraged, due to large amount of work
// involved in copying. Use pass-by-reference to avoid the copy.
CBitmap(CBitmap const &original):
m_pImageData(new char[num_channels * original.m_width * original.m_height]),
m_width(original.m_width),
m_height(original.m_height)
{
CopyImageDataFrom(original.m_pImageData);
}
~CBitmap()
{
delete [] m_pImageData;
}
public:
// An assignment operator is defined with copy-semantics. When an
// allocation error occurs, an exception is thrown (which should
// be caught by the client, and the object destructed) and the
// original data is preserved, to satisfy the invariant (m_pImageData != 0).
// post-condition: this bitmap becomes a uniform copy of the original.
// exception: failed allocation will cause the image data to be unchanged.
CBitmap& operator=(CBitmap const &original)
{
if ( this == &original )
{
return *this;
}
try
{
char *new_image_data = new char[num_channels * original.m_width * original.m_height];
delete [] m_pImageData;
m_pImageData = new_image_data;
m_width = original.m_width;
m_height = original.m_height;
CopyImageDataFrom(original.m_pImageData);
}
catch ( ... )
{
throw std::runtime_error( "assignment failed, original data conserved" );
}
return *this;
}
public:
bool SetPixel(unsigned int x, unsigned int y, char R, char G, char B)
{
if ( ! IsWithinBitmap(x, y) )
{
return false;
}
unsigned int pixel_index = ImageCoordinateToPixelIndex(x, y);
SetColorValueAtIndex( pixel_index, R, G, B );
return true;
}
bool GetPixel(unsigned int x, unsigned int y, char& R, char& G, char& B)
{
if ( ! IsWithinBitmap(x, y) )
{
return false;
}
unsigned int pixel_index = ImageCoordinateToPixelIndex(x, y);
GetColorValueAtIndex( pixel_index, R, G, B );
return true;
}
void Fill(char R, char G, char B)
{
for(unsigned int pixel_index = 0;
pixel_index < m_height * m_width * num_channels;
pixel_index += num_channels)
SetColorValueAtIndex( pixel_index, R, G, B );
}
private:
// An alternative to status flags is the use of exceptions.
bool IsWithinBitmap(unsigned int x, unsigned int y)
{
return y < m_height && x < m_width;
}
unsigned int ImageCoordinateToPixelIndex(unsigned int x, unsigned int y)
{
return (y * m_width + x) * num_channels;
}
void SetColorValueAtIndex(unsigned int pixel_index, char R, char G, char B)
{
m_pImageData[pixel_index + red_index] = R;
m_pImageData[pixel_index + green_index] = G;
m_pImageData[pixel_index + blue_index] = B;
}
void GetColorValueAtIndex(unsigned int pixel_index, char R, char G, char B)
{
R = m_pImageData[pixel_index + red_index];
G = m_pImageData[pixel_index + green_index];
B = m_pImageData[pixel_index + blue_index];
}
void CopyImageDataFrom(char *source)
{
// An alternative implementation using memcpy would be more efficient
// on almost all platforms.
for (unsigned int i = 0; i < num_channels * m_width * m_height; ++i)
m_pImageData[i] = source[i];
}
};