f09e17c3c5
- remove obsolete svn '$Id$' tags from all source files - update .fl files and generated files accordingly - replace 'http://www.fltk.org' URL's with 'https://...' - replace bug report URL 'str.php' with 'bugs.php' - remove trailing whitespace - fix other whitespace errors flagged by Git - add and/or fix missing or wrong standard headers - convert tabs to spaces in all source files The only relevant code changes are in the fluid/ folder where some .fl files and other source files were used to generate the '$Id' headers and footers.
477 lines
13 KiB
C++
477 lines
13 KiB
C++
//
|
|
// Fl_BMP_Image class for the Fast Light Tool Kit (FLTK).
|
|
//
|
|
// Copyright 2011-2020 by Bill Spitzak and others.
|
|
// Copyright 1997-2010 by Easy Software Products.
|
|
// Image support by Matthias Melcher, Copyright 2000-2009.
|
|
//
|
|
// This library is free software. Distribution and use rights are outlined in
|
|
// the file "COPYING" which should have been included with this file. If this
|
|
// file is missing or damaged, see the license at:
|
|
//
|
|
// https://www.fltk.org/COPYING.php
|
|
//
|
|
// Please see the following page on how to report bugs and issues:
|
|
//
|
|
// https://www.fltk.org/bugs.php
|
|
//
|
|
|
|
//
|
|
// Include necessary header files...
|
|
//
|
|
|
|
#include <FL/Fl_BMP_Image.H>
|
|
#include "Fl_Image_Reader.h"
|
|
#include <FL/fl_utf8.h>
|
|
#include <FL/Fl.H>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
|
|
//
|
|
// BMP definitions...
|
|
//
|
|
|
|
#ifndef BI_RGB
|
|
# define BI_RGB 0 // No compression - straight BGR data
|
|
# define BI_RLE8 1 // 8-bit run-length compression
|
|
# define BI_RLE4 2 // 4-bit run-length compression
|
|
# define BI_BITFIELDS 3 // RGB bitmap with RGB masks
|
|
#endif // !BI_RGB
|
|
|
|
|
|
/**
|
|
\brief This constructor loads the named BMP image from the given BMP filename.
|
|
|
|
The destructor frees all memory and server resources that are used by
|
|
the image.
|
|
|
|
Use Fl_Image::fail() to check if Fl_BMP_Image failed to load. fail() returns
|
|
ERR_FILE_ACCESS if the file could not be opened or read, ERR_FORMAT if the
|
|
BMP format could not be decoded, and ERR_NO_IMAGE if the image could not
|
|
be loaded for another reason.
|
|
|
|
\param[in] filename a full path and name pointing to a valid BMP file.
|
|
|
|
\see Fl_BMP_Image::Fl_BMP_Image(const char *imagename, const unsigned char *data)
|
|
*/
|
|
Fl_BMP_Image::Fl_BMP_Image(const char *filename) // I - File to read
|
|
: Fl_RGB_Image(0,0,0)
|
|
{
|
|
Fl_Image_Reader rdr;
|
|
if (rdr.open(filename) == -1) {
|
|
ld(ERR_FILE_ACCESS);
|
|
} else {
|
|
load_bmp_(rdr);
|
|
}
|
|
}
|
|
|
|
/**
|
|
\brief Read a BMP image from memory.
|
|
|
|
Construct an image from a block of memory inside the application. Fluid offers
|
|
"binary data" chunks as a great way to add image data into the C++ source code.
|
|
imagename can be NULL. If a name is given, the image is added to the list of
|
|
shared images and will be available by that name.
|
|
|
|
Use Fl_Image::fail() to check if Fl_BMP_Image failed to load. fail() returns
|
|
ERR_FILE_ACCESS if the image could not be read from memory, ERR_FORMAT if the
|
|
BMP format could not be decoded, and ERR_NO_IMAGE if the image could not
|
|
be loaded for another reason.
|
|
|
|
\param[in] imagename A name given to this image or NULL
|
|
\param[in] data Pointer to the start of the BMP image in memory. This code will not check for buffer overruns.
|
|
|
|
\see Fl_BMP_Image::Fl_BMP_Image(const char *filename)
|
|
\see Fl_Shared_Image
|
|
*/
|
|
Fl_BMP_Image::Fl_BMP_Image(const char *imagename, const unsigned char *data)
|
|
: Fl_RGB_Image(0,0,0)
|
|
{
|
|
Fl_Image_Reader rdr;
|
|
if (rdr.open(imagename, data) == -1) {
|
|
ld(ERR_FILE_ACCESS);
|
|
} else {
|
|
load_bmp_(rdr);
|
|
}
|
|
}
|
|
|
|
/*
|
|
This method reads BMP image data and creates an RGB or RGBA image. The BMP
|
|
format supports only 1 bit for alpha. To avoid code duplication, we use
|
|
an Fl_Image_Reader that reads data from either a file or from memory.
|
|
*/
|
|
void Fl_BMP_Image::load_bmp_(Fl_Image_Reader &rdr)
|
|
{
|
|
int info_size, // Size of info header
|
|
depth, // Depth of image (bits)
|
|
bDepth = 3, // Depth of image (bytes)
|
|
compression, // Type of compression
|
|
colors_used, // Number of colors used
|
|
x, y, // Looping vars
|
|
color, // Color of RLE pixel
|
|
repcount, // Number of times to repeat
|
|
temp, // Temporary color
|
|
align, // Alignment bytes
|
|
dataSize, // number of bytes in image data set
|
|
row_order, // 1 = normal; -1 = flipped row order
|
|
start_y, // Beginning Y
|
|
end_y; // Ending Y
|
|
long offbits; // Offset to image data
|
|
uchar bit, // Bit in image
|
|
byte; // Byte in image
|
|
uchar *ptr; // Pointer into pixels
|
|
uchar colormap[256][3]; // Colormap
|
|
uchar havemask; // Single bit mask follows image data
|
|
int use_5_6_5; // Use 5:6:5 for R:G:B channels in 16 bit images
|
|
|
|
// Reader is already open at this point.
|
|
|
|
// Get the header...
|
|
byte = rdr.read_byte(); // Check "BM" sync chars
|
|
bit = rdr.read_byte();
|
|
if (byte != 'B' || bit != 'M') {
|
|
ld(ERR_FORMAT);
|
|
return;
|
|
}
|
|
|
|
rdr.read_dword(); // Skip size
|
|
rdr.read_word(); // Skip reserved stuff
|
|
rdr.read_word();
|
|
offbits = (long)rdr.read_dword();// Read offset to image data
|
|
|
|
// Then the bitmap information...
|
|
info_size = rdr.read_dword();
|
|
|
|
// printf("offbits = %ld, info_size = %d\n", offbits, info_size);
|
|
|
|
havemask = 0;
|
|
row_order = -1;
|
|
use_5_6_5 = 0;
|
|
|
|
if (info_size < 40) {
|
|
// Old Windows/OS2 BMP header...
|
|
w(rdr.read_word());
|
|
h(rdr.read_word());
|
|
rdr.read_word();
|
|
depth = rdr.read_word();
|
|
compression = BI_RGB;
|
|
colors_used = 0;
|
|
|
|
repcount = info_size - 12;
|
|
} else {
|
|
// New BMP header...
|
|
w(rdr.read_long());
|
|
// If the height is negative, the row order is flipped
|
|
temp = rdr.read_long();
|
|
if (temp < 0) row_order = 1;
|
|
h(abs(temp));
|
|
rdr.read_word();
|
|
depth = rdr.read_word();
|
|
compression = rdr.read_dword();
|
|
dataSize = rdr.read_dword();
|
|
rdr.read_long();
|
|
rdr.read_long();
|
|
colors_used = rdr.read_dword();
|
|
rdr.read_dword();
|
|
|
|
repcount = info_size - 40;
|
|
|
|
if (!compression && depth>=8 && w()>32/depth) {
|
|
int Bpp = depth/8;
|
|
int maskSize = (((w()*Bpp+3)&~3)*h()) + (((((w()+7)/8)+3)&~3)*h());
|
|
if (maskSize==2*dataSize) {
|
|
havemask = 1;
|
|
h(h()/2);
|
|
bDepth = 4;
|
|
}
|
|
}
|
|
}
|
|
|
|
// printf("w() = %d, h() = %d, depth = %d, compression = %d, colors_used = %d, repcount = %d\n",
|
|
// w(), h(), depth, compression, colors_used, repcount);
|
|
|
|
// Skip remaining header bytes...
|
|
while (repcount > 0) {
|
|
rdr.read_byte();
|
|
repcount --;
|
|
}
|
|
|
|
// Check header data...
|
|
if (!w() || !h() || !depth) {
|
|
w(0); h(0); d(0); ld(ERR_FORMAT);
|
|
return;
|
|
}
|
|
|
|
// Get colormap...
|
|
if (colors_used == 0 && depth <= 8)
|
|
colors_used = 1 << depth;
|
|
|
|
for (repcount = 0; repcount < colors_used; repcount ++) {
|
|
// Read BGR color...
|
|
colormap[repcount][0] = rdr.read_byte();
|
|
colormap[repcount][1] = rdr.read_byte();
|
|
colormap[repcount][2] = rdr.read_byte();
|
|
|
|
// Skip pad byte for new BMP files...
|
|
if (info_size > 12) rdr.read_byte();
|
|
}
|
|
|
|
// Read first dword of colormap. It tells us if 5:5:5 or 5:6:5 for 16 bit
|
|
if (depth == 16)
|
|
use_5_6_5 = (rdr.read_dword() == 0xf800);
|
|
|
|
// Set byte depth for RGBA images
|
|
if (depth == 32)
|
|
bDepth=4;
|
|
|
|
// Setup image and buffers...
|
|
d(bDepth);
|
|
if (offbits) rdr.seek(offbits);
|
|
|
|
if (((size_t)w()) * h() * d() > max_size() ) {
|
|
Fl::warning("BMP file \"%s\" is too large!\n", rdr.name());
|
|
w(0); h(0); d(0); ld(ERR_FORMAT);
|
|
return;
|
|
}
|
|
array = new uchar[w() * h() * d()];
|
|
alloc_array = 1;
|
|
|
|
// Read the image data...
|
|
color = 0;
|
|
repcount = 0;
|
|
align = 0;
|
|
byte = 0;
|
|
temp = 0;
|
|
|
|
if (row_order < 0) {
|
|
start_y = h() - 1;
|
|
end_y = -1;
|
|
} else {
|
|
start_y = 0;
|
|
end_y = h();
|
|
}
|
|
|
|
for (y = start_y; y != end_y; y += row_order) {
|
|
ptr = (uchar *)array + y * w() * d();
|
|
|
|
switch (depth)
|
|
{
|
|
case 1 : // Bitmap
|
|
for (x = w(), bit = 128; x > 0; x --) {
|
|
if (bit == 128) byte = rdr.read_byte();
|
|
|
|
if (byte & bit) {
|
|
*ptr++ = colormap[1][2];
|
|
*ptr++ = colormap[1][1];
|
|
*ptr++ = colormap[1][0];
|
|
} else {
|
|
*ptr++ = colormap[0][2];
|
|
*ptr++ = colormap[0][1];
|
|
*ptr++ = colormap[0][0];
|
|
}
|
|
|
|
if (bit > 1)
|
|
bit >>= 1;
|
|
else
|
|
bit = 128;
|
|
}
|
|
|
|
// Read remaining bytes to align to 32 bits...
|
|
for (temp = (w() + 7) / 8; temp & 3; temp ++) {
|
|
rdr.read_byte();
|
|
}
|
|
break;
|
|
|
|
case 4 : // 16-color
|
|
for (x = w(), bit = 0xf0; x > 0; x --) {
|
|
// Get a new repcount as needed...
|
|
if (repcount == 0) {
|
|
if (compression != BI_RLE4) {
|
|
repcount = 2;
|
|
color = -1;
|
|
} else {
|
|
while (align > 0) {
|
|
align --;
|
|
rdr.read_byte();
|
|
}
|
|
|
|
if ((repcount = rdr.read_byte()) == 0) {
|
|
if ((repcount = rdr.read_byte()) == 0) {
|
|
// End of line...
|
|
x ++;
|
|
continue;
|
|
} else if (repcount == 1) {
|
|
// End of image...
|
|
break;
|
|
} else if (repcount == 2) {
|
|
// Delta...
|
|
repcount = rdr.read_byte() * rdr.read_byte() * w();
|
|
color = 0;
|
|
} else {
|
|
// Absolute...
|
|
color = -1;
|
|
align = ((4 - (repcount & 3)) / 2) & 1;
|
|
}
|
|
} else {
|
|
color = rdr.read_byte();
|
|
}
|
|
}
|
|
}
|
|
|
|
// Get a new color as needed...
|
|
repcount --;
|
|
|
|
// Extract the next pixel...
|
|
if (bit == 0xf0) {
|
|
// Get the next color byte as needed...
|
|
if (color < 0) temp = rdr.read_byte();
|
|
else temp = color;
|
|
|
|
// Copy the color value...
|
|
*ptr++ = colormap[(temp >> 4) & 15][2];
|
|
*ptr++ = colormap[(temp >> 4) & 15][1];
|
|
*ptr++ = colormap[(temp >> 4) & 15][0];
|
|
|
|
bit = 0x0f;
|
|
} else {
|
|
bit = 0xf0;
|
|
|
|
// Copy the color value...
|
|
*ptr++ = colormap[temp & 15][2];
|
|
*ptr++ = colormap[temp & 15][1];
|
|
*ptr++ = colormap[temp & 15][0];
|
|
}
|
|
|
|
}
|
|
|
|
if (!compression) {
|
|
// Read remaining bytes to align to 32 bits...
|
|
for (temp = (w() + 1) / 2; temp & 3; temp ++) {
|
|
rdr.read_byte();
|
|
}
|
|
}
|
|
break;
|
|
|
|
case 8 : // 256-color
|
|
for (x = w(); x > 0; x --) {
|
|
// Get a new repcount as needed...
|
|
if (compression != BI_RLE8) {
|
|
repcount = 1;
|
|
color = -1;
|
|
}
|
|
|
|
if (repcount == 0) {
|
|
while (align > 0) {
|
|
align --;
|
|
rdr.read_byte();
|
|
}
|
|
|
|
if ((repcount = rdr.read_byte()) == 0) {
|
|
if ((repcount = rdr.read_byte()) == 0) {
|
|
// End of line...
|
|
x ++;
|
|
continue;
|
|
} else if (repcount == 1) {
|
|
// End of image...
|
|
break;
|
|
} else if (repcount == 2) {
|
|
// Delta...
|
|
repcount = rdr.read_byte() * rdr.read_byte() * w();
|
|
color = 0;
|
|
} else {
|
|
// Absolute...
|
|
color = -1;
|
|
align = (2 - (repcount & 1)) & 1;
|
|
}
|
|
} else {
|
|
color = rdr.read_byte();
|
|
}
|
|
}
|
|
|
|
// Get a new color as needed...
|
|
if (color < 0) temp = rdr.read_byte();
|
|
else temp = color;
|
|
|
|
repcount --;
|
|
|
|
// Copy the color value...
|
|
*ptr++ = colormap[temp][2];
|
|
*ptr++ = colormap[temp][1];
|
|
*ptr++ = colormap[temp][0];
|
|
if (havemask) ptr++;
|
|
}
|
|
|
|
if (!compression) {
|
|
// Read remaining bytes to align to 32 bits...
|
|
for (temp = w(); temp & 3; temp ++) {
|
|
rdr.read_byte();
|
|
}
|
|
}
|
|
break;
|
|
|
|
case 16 : // 16-bit 5:5:5 or 5:6:5 RGB
|
|
for (x = w(); x > 0; x --, ptr += bDepth) {
|
|
uchar b = rdr.read_byte(), a = rdr.read_byte() ;
|
|
if (use_5_6_5) {
|
|
ptr[2] = (uchar)(( b << 3 ) & 0xf8);
|
|
ptr[1] = (uchar)(((a << 5) & 0xe0) | ((b >> 3) & 0x1c));
|
|
ptr[0] = (uchar)(a & 0xf8);
|
|
} else {
|
|
ptr[2] = (uchar)((b << 3) & 0xf8);
|
|
ptr[1] = (uchar)(((a << 6) & 0xc0) | ((b >> 2) & 0x38));
|
|
ptr[0] = (uchar)((a<<1) & 0xf8);
|
|
}
|
|
}
|
|
|
|
// Read remaining bytes to align to 32 bits...
|
|
for (temp = w() * 2; temp & 3; temp ++) {
|
|
rdr.read_byte();
|
|
}
|
|
break;
|
|
|
|
case 24 : // 24-bit RGB
|
|
for (x = w(); x > 0; x --, ptr += bDepth) {
|
|
ptr[2] = rdr.read_byte();
|
|
ptr[1] = rdr.read_byte();
|
|
ptr[0] = rdr.read_byte();
|
|
}
|
|
|
|
// Read remaining bytes to align to 32 bits...
|
|
for (temp = w() * 3; temp & 3; temp ++) {
|
|
rdr.read_byte();
|
|
}
|
|
break;
|
|
|
|
case 32 : // 32-bit RGBA
|
|
for (x = w(); x > 0; x --, ptr += bDepth) {
|
|
ptr[2] = rdr.read_byte();
|
|
ptr[1] = rdr.read_byte();
|
|
ptr[0] = rdr.read_byte();
|
|
ptr[3] = rdr.read_byte();
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (havemask) {
|
|
for (y = h() - 1; y >= 0; y --) {
|
|
ptr = (uchar *)array + y * w() * d() + 3;
|
|
for (x = w(), bit = 128; x > 0; x --, ptr+=bDepth) {
|
|
if (bit == 128) byte = rdr.read_byte();
|
|
if (byte & bit)
|
|
*ptr = 0;
|
|
else
|
|
*ptr = 255;
|
|
if (bit > 1)
|
|
bit >>= 1;
|
|
else
|
|
bit = 128;
|
|
}
|
|
// Read remaining bytes to align to 32 bits...
|
|
for (temp = (w() + 7) / 8; temp & 3; temp ++)
|
|
rdr.read_byte();
|
|
}
|
|
}
|
|
// File is closed when returning...
|
|
}
|