* contrib/hbbmp/core.c
! fixed copy & past typos reported by Grigory Filatov. Thanks a lot.
877 lines
24 KiB
C
877 lines
24 KiB
C
/*
|
|
* BMP image library for Harbour
|
|
*
|
|
* Copyright 2025 Przemyslaw Czerpak <druzus /at/ priv.onet.pl>
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2, or (at your option)
|
|
* any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; see the file LICENSE.txt. If not, write to
|
|
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
|
* Boston, MA 02110-1301 USA (or visit https://www.gnu.org/licenses/).
|
|
*
|
|
* As a special exception, the Harbour Project gives permission for
|
|
* additional uses of the text contained in its release of Harbour.
|
|
*
|
|
* The exception is that, if you link the Harbour libraries with other
|
|
* files to produce an executable, this does not by itself cause the
|
|
* resulting executable to be covered by the GNU General Public License.
|
|
* Your use of that executable is in no way restricted on account of
|
|
* linking the Harbour library code into it.
|
|
*
|
|
* This exception does not however invalidate any other reasons why
|
|
* the executable file might be covered by the GNU General Public License.
|
|
*
|
|
* This exception applies only to the code released by the Harbour
|
|
* Project under the name Harbour. If you copy code from other
|
|
* Harbour Project or Free Software Foundation releases into a copy of
|
|
* Harbour, as the General Public License permits, the exception does
|
|
* not apply to the code that you add in this way. To avoid misleading
|
|
* anyone as to the status of such modified files, you must delete
|
|
* this exception notice from them.
|
|
*
|
|
* If you write modifications of your own for Harbour, it is your choice
|
|
* whether to permit this exception to apply to your modifications.
|
|
* If you do not wish that, delete this exception notice.
|
|
*
|
|
*/
|
|
|
|
#include "hbbmp.h"
|
|
#include "hbapi.h"
|
|
#include "hbapifs.h"
|
|
#include "hbapierr.h"
|
|
|
|
void hb_bmp_free( PHB_BMPINFO pBMP )
|
|
{
|
|
if( pBMP->data )
|
|
hb_xfree( pBMP->data );
|
|
hb_xfree( pBMP );
|
|
}
|
|
|
|
PHB_BMPINFO hb_bmp_new( int width, int height, int depth, int dpi, int * piError )
|
|
{
|
|
PHB_BMPINFO pBMP = NULL;
|
|
HB_BOOL fromtop = height < 0;
|
|
|
|
if( piError )
|
|
*piError = 0;
|
|
|
|
if( fromtop )
|
|
height = -height;
|
|
if( dpi == 0 )
|
|
dpi = HB_BMP_DPI_DEFAULT;
|
|
|
|
if( width < 1 || height < 1 || width > 0x10000 || height > 0x10000 )
|
|
{
|
|
if( piError )
|
|
*piError = HB_BMP_ERROR_SIZE;
|
|
}
|
|
else
|
|
{
|
|
int rowlen;
|
|
|
|
switch( depth )
|
|
{
|
|
case 0:
|
|
depth = HB_BMP_DEPTH_DEFAULT;
|
|
/* fallthrough */
|
|
case 1:
|
|
/* 2 color depth BMP is not officially supported,
|
|
uncomment line below if you need it */
|
|
/* case 2: */
|
|
case 4:
|
|
case 8:
|
|
case 16:
|
|
case 24:
|
|
case 32:
|
|
break;
|
|
default:
|
|
if( piError )
|
|
*piError = HB_BMP_ERROR_DEPTH;
|
|
return NULL;
|
|
}
|
|
pBMP = ( PHB_BMPINFO ) hb_xgrabz( sizeof( HB_BMPINFO ) );
|
|
rowlen = ( ( width * depth + 31 ) >> 5 ) << 2;
|
|
|
|
pBMP->error = 0;
|
|
pBMP->width = width;
|
|
pBMP->height = height;
|
|
pBMP->dpi = dpi;
|
|
pBMP->depth = depth;
|
|
pBMP->clrused = 0;
|
|
pBMP->rowlen = rowlen;
|
|
pBMP->fromtop = fromtop;
|
|
pBMP->data = ( HB_BYTE * ) hb_xgrabz( rowlen * height );
|
|
}
|
|
return pBMP;
|
|
}
|
|
|
|
PHB_BMPINFO hb_bmp_copy( PHB_BMPINFO pBMP )
|
|
{
|
|
PHB_BMPINFO pBMPnew = ( PHB_BMPINFO ) hb_xgrab( sizeof( HB_BMPINFO ) );
|
|
|
|
memcpy( pBMPnew, pBMP, sizeof( HB_BMPINFO ) );
|
|
pBMPnew->data = ( HB_BYTE * ) hb_xgrab( pBMP->rowlen * pBMP->height );
|
|
memcpy( pBMPnew->data, pBMP->data, pBMP->rowlen * pBMP->height );
|
|
|
|
pBMPnew->error = 0;
|
|
|
|
return pBMPnew;
|
|
}
|
|
|
|
HB_BYTE * hb_bmp_bitmapptr( PHB_BMPINFO pBMP, HB_SIZE * pnSize )
|
|
{
|
|
if( pnSize )
|
|
*pnSize = pBMP->rowlen * pBMP->height;
|
|
return pBMP->data;
|
|
}
|
|
|
|
void hb_bmp_seterror( PHB_BMPINFO pBMP, int error )
|
|
{
|
|
pBMP->error = error;
|
|
}
|
|
|
|
int hb_bmp_error( PHB_BMPINFO pBMP )
|
|
{
|
|
return pBMP->error;
|
|
}
|
|
|
|
int hb_bmp_width( PHB_BMPINFO pBMP )
|
|
{
|
|
return pBMP->width;
|
|
}
|
|
|
|
int hb_bmp_height( PHB_BMPINFO pBMP )
|
|
{
|
|
return pBMP->height;
|
|
}
|
|
|
|
int hb_bmp_depth( PHB_BMPINFO pBMP )
|
|
{
|
|
return pBMP->depth;
|
|
}
|
|
|
|
HB_MAXINT hb_bmp_color( PHB_BMPINFO pBMP, int r, int g, int b, int a )
|
|
{
|
|
HB_MAXINT iColor = -1;
|
|
|
|
pBMP->error = 0;
|
|
|
|
if( ( r & 0xFF ) != r || ( g & 0xFF ) != g ||
|
|
( b & 0xFF ) != b || ( a & 0xFF ) != a )
|
|
pBMP->error = HB_BMP_ERROR_COLORVALUE;
|
|
else if( pBMP->depth == 32 )
|
|
iColor = b | ( g << 8 ) | ( r << 16 ) | ( a << 24 );
|
|
else if( pBMP->depth == 24 )
|
|
iColor = b | ( g << 8 ) | ( r << 16 );
|
|
/* 16 color depth does not have RGB mapping in BMP format
|
|
* color indexes have to be used directly and their RGB
|
|
* values defined outside BMP structure
|
|
*/
|
|
else if( pBMP->depth <= 8 )
|
|
{
|
|
int i;
|
|
for( i = 0; i < pBMP->clrused; ++i )
|
|
{
|
|
if( pBMP->palette[ i ].blue == b &&
|
|
pBMP->palette[ i ].green == g &&
|
|
pBMP->palette[ i ].red == r &&
|
|
pBMP->palette[ i ].alpha == a )
|
|
{
|
|
iColor = i;
|
|
break;
|
|
}
|
|
}
|
|
if( iColor == -1 )
|
|
{
|
|
if( pBMP->clrused < 1 << pBMP->depth )
|
|
{
|
|
iColor = pBMP->clrused++;
|
|
pBMP->palette[ iColor ].blue = b;
|
|
pBMP->palette[ iColor ].green = g;
|
|
pBMP->palette[ iColor ].red = r;
|
|
pBMP->palette[ iColor ].alpha = a;
|
|
}
|
|
else
|
|
pBMP->error = HB_BMP_ERROR_PALETTEFULL;
|
|
}
|
|
}
|
|
else
|
|
pBMP->error = HB_BMP_ERROR_COLORINDEX;
|
|
|
|
return iColor;
|
|
}
|
|
|
|
HB_BOOL hb_bmp_color2rgb( PHB_BMPINFO pBMP, HB_MAXINT clr, int * r, int * g, int * b, int * a )
|
|
{
|
|
pBMP->error = HB_BMP_ERROR_COLORINDEX;
|
|
* b = * g = * r = * a = -1;
|
|
|
|
if( clr >= 0 && clr <= HB_LL( 0xFFFFFFFF ) )
|
|
{
|
|
switch( pBMP->depth )
|
|
{
|
|
case 1:
|
|
case 2:
|
|
case 4:
|
|
case 8:
|
|
if( clr < ( HB_MAXINT ) pBMP->clrused )
|
|
{
|
|
* b = pBMP->palette[ clr ].blue;
|
|
* g = pBMP->palette[ clr ].green;
|
|
* r = pBMP->palette[ clr ].red;
|
|
* a = pBMP->palette[ clr ].alpha;
|
|
pBMP->error = 0;
|
|
}
|
|
break;
|
|
/* 16 color depth does not have RGB mapping in BMP format
|
|
* color indexes have to be used directly and their RGB
|
|
* values defined outside BMP structure
|
|
*/
|
|
case 24:
|
|
/* ignore alpha channel */
|
|
clr &= 0xFFFFFF;
|
|
/* fallthrough */
|
|
case 32:
|
|
* b = clr & 0xFF;
|
|
* g = ( clr >> 8 ) & 0xFF;
|
|
* r = ( clr >> 16 ) & 0xFF;
|
|
* a = ( clr >> 24 ) & 0xFF;
|
|
pBMP->error = 0;
|
|
break;
|
|
}
|
|
}
|
|
return pBMP->error == 0;
|
|
}
|
|
|
|
HB_BOOL hb_bmp_putpixel( PHB_BMPINFO pBMP, int x, int y, HB_MAXINT clr )
|
|
{
|
|
if( x < 0 || x >= pBMP->width || y < 0 || y >= pBMP->height )
|
|
pBMP->error = HB_BMP_ERROR_RANGE;
|
|
else if( clr < 0 || ( clr > ( pBMP->depth <= 8 ? pBMP->clrused :
|
|
( pBMP->depth == 16 ? 0xFFFF :
|
|
HB_LL( 0xFFFFFFFF ) ) ) ) )
|
|
pBMP->error = HB_BMP_ERROR_COLORINDEX;
|
|
else
|
|
{
|
|
int index = ( ( ( pBMP->fromtop ? y : pBMP->height - y - 1 ) *
|
|
pBMP->rowlen ) << 3 ) + ( x * pBMP->depth );
|
|
HB_BYTE * ptr = &pBMP->data[ index >> 3 ];
|
|
int shift = -1;
|
|
|
|
switch( pBMP->depth )
|
|
{
|
|
case 1:
|
|
shift = 0x07 ^ ( x & 0x07 );
|
|
break;
|
|
case 2:
|
|
shift = ( 0x03 ^ ( x & 0x03 ) ) << 1;
|
|
break;
|
|
case 4:
|
|
shift = ( 0x01 ^ ( x & 0x01 ) ) << 2;
|
|
break;
|
|
case 8:
|
|
*ptr = ( HB_BYTE ) clr;
|
|
break;
|
|
case 16:
|
|
HB_PUT_LE_UINT16( ptr, clr );
|
|
break;
|
|
case 24:
|
|
HB_PUT_LE_UINT24( ptr, clr );
|
|
break;
|
|
case 32:
|
|
HB_PUT_LE_UINT32( ptr, clr );
|
|
break;
|
|
}
|
|
if( shift >= 0 )
|
|
*ptr = ( HB_BYTE ) ( ( *ptr & ~( ( ( 0x01 << pBMP->depth ) - 1 ) << shift ) ) | ( clr << shift ) );
|
|
pBMP->error = 0;
|
|
}
|
|
return pBMP->error == 0;
|
|
}
|
|
|
|
HB_MAXINT hb_bmp_getpixel( PHB_BMPINFO pBMP, int x, int y )
|
|
{
|
|
HB_MAXINT clr = -1;
|
|
|
|
if( x < 0 || x >= pBMP->width || y < 0 || y >= pBMP->height )
|
|
pBMP->error = HB_BMP_ERROR_RANGE;
|
|
else
|
|
{
|
|
int index = ( ( ( pBMP->fromtop ? y : pBMP->height - y - 1 ) *
|
|
pBMP->rowlen ) << 3 ) + ( x * pBMP->depth );
|
|
HB_BYTE * ptr = &pBMP->data[ index >> 3 ];
|
|
int shift = -1;
|
|
|
|
switch( pBMP->depth )
|
|
{
|
|
case 1:
|
|
shift = 0x07 ^ ( x & 0x07 );
|
|
break;
|
|
case 2:
|
|
shift = ( 0x03 ^ ( x & 0x03 ) ) << 1;
|
|
break;
|
|
case 4:
|
|
shift = ( 0x01 ^ ( x & 0x01 ) ) << 2;
|
|
break;
|
|
case 8:
|
|
clr = *ptr;
|
|
break;
|
|
case 16:
|
|
clr = HB_GET_LE_UINT16( ptr );
|
|
break;
|
|
case 24:
|
|
clr = HB_GET_LE_UINT24( ptr );
|
|
break;
|
|
case 32:
|
|
clr = HB_GET_LE_UINT32( ptr );
|
|
break;
|
|
}
|
|
if( shift >= 0 )
|
|
clr = ( *ptr & ( ( ( 0x01 << pBMP->depth ) - 1 ) << shift ) ) >> shift;
|
|
pBMP->error = 0;
|
|
}
|
|
return clr;
|
|
}
|
|
|
|
void hb_bmp_line( PHB_BMPINFO pBMP, int x1, int y1, int x2, int y2, HB_MAXINT clr )
|
|
{
|
|
if( clr < 0 || ( clr > ( pBMP->depth <= 8 ? pBMP->clrused :
|
|
( pBMP->depth == 16 ? 0xFFFF :
|
|
HB_LL( 0xFFFFFFFF ) ) ) ) )
|
|
pBMP->error = HB_BMP_ERROR_COLORINDEX;
|
|
else
|
|
{
|
|
double dd;
|
|
int dx, dy, x, y;
|
|
|
|
dx = x1 >= x2 ? x1 - x2 : x2 - x1;
|
|
dy = y1 >= y2 ? y1 - y2 : y2 - y1;
|
|
|
|
if( dx >= dy || dy == 0 ? x1 > x2 : y1 > y2 || dx == 0 )
|
|
{
|
|
int nn = x1;
|
|
x1 = x2;
|
|
x2 = nn;
|
|
nn = y1;
|
|
y1 = y2;
|
|
y2 = nn;
|
|
}
|
|
|
|
if( dy == 0 )
|
|
{
|
|
for( x = HB_MAX( x1, 0 ); x <= x2 && x < pBMP->width; ++x )
|
|
hb_bmp_putpixel( pBMP, x, y1, clr );
|
|
}
|
|
else if( dx == 0 )
|
|
{
|
|
for( y = HB_MAX( y1, 0 ); y <= y2 && y < pBMP->height; ++y )
|
|
hb_bmp_putpixel( pBMP, x1, y, clr );
|
|
}
|
|
else if( dx >= dy )
|
|
{
|
|
dd = ( double ) ( y2 - y1 ) / ( x2 - x1 );
|
|
for( x = HB_MAX( x1, 0 ); x <= x2 && x < pBMP->width; ++x )
|
|
hb_bmp_putpixel( pBMP, x, ( int ) ( dd * ( x - x1 ) + 0.50001 ) + y1, clr );
|
|
}
|
|
else
|
|
{
|
|
dd = ( double ) ( x2 - x1 ) / ( y2 - y1 );
|
|
for( y = HB_MAX( y1, 0 ); y <= y2 && y < pBMP->height; ++y )
|
|
hb_bmp_putpixel( pBMP, ( int ) ( dd * ( y - y1 ) + 0.50001 ) + x1, y, clr );
|
|
}
|
|
pBMP->error = 0;
|
|
}
|
|
}
|
|
|
|
void hb_bmp_rect( PHB_BMPINFO pBMP, int x, int y, int width, int height, HB_MAXINT clr, HB_BOOL fFill )
|
|
{
|
|
if( width < 0 )
|
|
{
|
|
x += width;
|
|
width = - width;
|
|
}
|
|
if( height < 0 )
|
|
{
|
|
y += height;
|
|
height = - height;
|
|
}
|
|
if( clr < 0 || ( clr > ( pBMP->depth <= 8 ? pBMP->clrused :
|
|
( pBMP->depth == 16 ? 0xFFFF :
|
|
HB_LL( 0xFFFFFFFF ) ) ) ) )
|
|
pBMP->error = HB_BMP_ERROR_COLORINDEX;
|
|
else if( width < 0 || height < 0 ||
|
|
x >= pBMP->width || y >= pBMP->height ||
|
|
x + width < 0 || y + height < 0 )
|
|
pBMP->error = HB_BMP_ERROR_RANGE;
|
|
else
|
|
{
|
|
int x1, y1, x2, y2, xx, yy;
|
|
|
|
x1 = HB_MAX( x, 0 );
|
|
y1 = HB_MAX( y, 0 );
|
|
x2 = x1 + width >= pBMP->width ? pBMP->width : x1 + width;
|
|
y2 = y1 + height >= pBMP->height ? pBMP->height : y1 + height;
|
|
if( fFill )
|
|
{
|
|
for( xx = x1; xx < x2; ++xx )
|
|
{
|
|
for( yy = y1; yy < y2; ++yy )
|
|
hb_bmp_putpixel( pBMP, xx, yy, clr );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if( y >= 0 )
|
|
{
|
|
for( xx = x1; xx < x2; ++xx )
|
|
hb_bmp_putpixel( pBMP, xx, y, clr );
|
|
}
|
|
if( x >= 0 )
|
|
{
|
|
for( yy = y1; yy < y2; ++yy )
|
|
hb_bmp_putpixel( pBMP, x, yy, clr );
|
|
}
|
|
if( y2 < pBMP->height )
|
|
{
|
|
for( xx = x1; xx < x2; ++xx )
|
|
hb_bmp_putpixel( pBMP, xx, y2 - 1, clr );
|
|
}
|
|
if( x2 < pBMP->width )
|
|
{
|
|
for( yy = y1; yy < y2; ++yy )
|
|
hb_bmp_putpixel( pBMP, x2 - 1, yy, clr );
|
|
}
|
|
}
|
|
pBMP->error = 0;
|
|
}
|
|
}
|
|
|
|
PHB_BMPINFO hb_bmp_decode( const HB_BYTE * data, HB_SIZE size, int * piError )
|
|
{
|
|
PHB_BMPINFO pBMP = NULL;
|
|
int iError = 0;
|
|
|
|
if( ! data || size < HB_BMP_FILEHEADER_SIZE + HB_BMP_INFOHEADER_MINSIZE )
|
|
iError = HB_BMP_ERROR_CORRUPT;
|
|
else
|
|
{
|
|
PHB_BMPHEADER header = ( PHB_BMPHEADER ) data;
|
|
if( header->signature[ 0 ] != 'B' || header->signature[ 1 ] != 'M' )
|
|
iError = HB_BMP_ERROR_CORRUPT;
|
|
else
|
|
{
|
|
/* file_size in BMP headers is often wrong so I do not check it
|
|
* int file_size = HB_GET_LE_INT32( header->file_size );
|
|
*/
|
|
int bmpoffset = HB_GET_LE_INT32( header->bmpoffset ),
|
|
headersize = HB_GET_LE_INT32( header->headersize ),
|
|
width = 0, height = 0, depth = 0, rowlen = 0, dpi = 0,
|
|
clrused = 0, palette_bytes = 0;
|
|
HB_BOOL fromtop;
|
|
|
|
if( size < ( HB_SIZE ) ( HB_BMP_FILEHEADER_SIZE +
|
|
HB_BMP_INFOHEADER_MINSIZE + headersize ) )
|
|
iError = HB_BMP_ERROR_CORRUPT;
|
|
else if( headersize == 12 || headersize == 16 )
|
|
{
|
|
PHB_BMPOS2HEADER header_os2 = ( PHB_BMPOS2HEADER ) header->file_size;
|
|
width = HB_GET_LE_UINT16( header_os2->width );
|
|
height = HB_GET_LE_UINT16( header_os2->height );
|
|
depth = HB_GET_LE_UINT16( header_os2->bitsperpixel );
|
|
if( depth <= 8 )
|
|
clrused = 0x01 << depth;
|
|
dpi = HB_BMP_DPI_DEFAULT;
|
|
palette_bytes = 3;
|
|
if( depth != 1 && depth != 2 && depth != 4 && depth != 8 && depth != 24 )
|
|
iError = HB_BMP_ERROR_CORRUPT;
|
|
}
|
|
else if( headersize == 40 || headersize == 52 || headersize == 56 ||
|
|
headersize == 108 || headersize == 124 )
|
|
{
|
|
width = HB_GET_LE_INT32( header->width );
|
|
height = HB_GET_LE_INT32( header->height );
|
|
dpi = ( int ) ( ( double ) HB_GET_LE_INT32( header->hresolution ) / 39.3701 + 0.5 );
|
|
depth = HB_GET_LE_UINT16( header->bitsperpixel );
|
|
if( depth <= 8 )
|
|
{
|
|
clrused = HB_GET_LE_INT32( header->clrused );
|
|
if( clrused == 0 )
|
|
clrused = 0x01 << depth;
|
|
}
|
|
palette_bytes = 4;
|
|
if( ( depth != 1 && depth != 2 && depth != 4 && depth != 8 &&
|
|
depth != 16 && depth != 24 && depth != 32 ) )
|
|
iError = HB_BMP_ERROR_CORRUPT;
|
|
else if( HB_GET_LE_INT32( header->compression ) != 0 )
|
|
iError = HB_BMP_ERROR_UNSUPPORTED;
|
|
}
|
|
else
|
|
iError = HB_BMP_ERROR_CORRUPT;
|
|
|
|
fromtop = height < 0;
|
|
if( fromtop )
|
|
height = -height;
|
|
|
|
rowlen = ( ( width * depth + 31 ) >> 5 ) << 2;
|
|
if( iError == 0 &&
|
|
( HB_BMP_FILEHEADER_SIZE + headersize + clrused * palette_bytes > bmpoffset ||
|
|
( HB_SIZE ) bmpoffset + rowlen * height > size ) )
|
|
iError = HB_BMP_ERROR_CORRUPT;
|
|
else if( iError == 0 )
|
|
{
|
|
pBMP = hb_bmp_new( width, fromtop ? -height : height, depth, dpi, &iError );
|
|
if( pBMP )
|
|
{
|
|
const HB_BYTE * ptr = data + HB_BMP_FILEHEADER_SIZE + headersize;
|
|
int idx, unused = 0;
|
|
for( idx = 0; idx < clrused; ++idx )
|
|
{
|
|
pBMP->palette[ idx ].blue = *ptr++;
|
|
pBMP->palette[ idx ].green = *ptr++;
|
|
pBMP->palette[ idx ].red = *ptr++;
|
|
if( palette_bytes == 4 )
|
|
pBMP->palette[ idx ].alpha = *ptr++;
|
|
if( pBMP->palette[ idx ].blue == 0 &&
|
|
pBMP->palette[ idx ].green == 0 &&
|
|
pBMP->palette[ idx ].red == 0 &&
|
|
pBMP->palette[ idx ].alpha == 0 )
|
|
++unused;
|
|
else if( unused > 1 )
|
|
unused = 1;
|
|
}
|
|
if( unused > 1 )
|
|
clrused -= unused - 1;
|
|
pBMP->clrused = clrused;
|
|
memcpy( pBMP->data, data + bmpoffset, rowlen * height );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if( piError )
|
|
*piError = iError;
|
|
|
|
return pBMP;
|
|
}
|
|
|
|
HB_BYTE * hb_bmp_encode( PHB_BMPINFO pBMP, HB_SIZE * pnSize )
|
|
{
|
|
int clrused, bmpoffset, bmpsize, file_size, idx, height, dpi;
|
|
PHB_BMPHEADER header;
|
|
HB_BYTE * data, * ptr;
|
|
|
|
/* always allocate area for whole palette for programs which
|
|
* do not check number of used colors in BMP header
|
|
*/
|
|
clrused = pBMP->depth > 8 ? 0 : 0x01 << pBMP->depth;
|
|
/* 4 byte alignment is not required inside file */
|
|
/* bmpoffset = ( sizeof( HB_BMPHEADER ) + clrused * 4 + 3 ) & ~0x03; */
|
|
bmpoffset = sizeof( HB_BMPHEADER ) + clrused * 4;
|
|
bmpsize = pBMP->rowlen * pBMP->height;
|
|
file_size = bmpoffset + bmpsize;
|
|
height = pBMP->fromtop ? -pBMP->height : pBMP->height;
|
|
dpi = ( int ) ( HB_BMP_INCHES_PER_METRE * pBMP->dpi + 0.5 );
|
|
|
|
/* when clrused is set in BMP header to value different then 0 or
|
|
* palette size some BMP viewers cannot show BMP correctly so
|
|
* I decided to comment code below and always store 0
|
|
*/
|
|
/*
|
|
if( clrused > 0 )
|
|
clrused = clrused > pBMP->clrused ? pBMP->clrused : 0;
|
|
*/
|
|
clrused = 0;
|
|
|
|
data = ( HB_BYTE * ) hb_xgrab( file_size + 1 );
|
|
memset( data, 0, sizeof( HB_BMPHEADER ) );
|
|
|
|
header = ( PHB_BMPHEADER ) data;
|
|
header->signature[ 0 ] = 'B';
|
|
header->signature[ 1 ] = 'M';
|
|
HB_PUT_LE_UINT32( header->file_size, file_size );
|
|
HB_PUT_LE_UINT16( header->reserved1, 0 );
|
|
HB_PUT_LE_UINT16( header->reserved2, 0 );
|
|
HB_PUT_LE_UINT32( header->bmpoffset, bmpoffset );
|
|
HB_PUT_LE_UINT32( header->headersize, sizeof( HB_BMPHEADER ) - HB_BMP_FILEHEADER_SIZE );
|
|
HB_PUT_LE_UINT32( header->width, pBMP->width );
|
|
HB_PUT_LE_UINT32( header->height, height );
|
|
HB_PUT_LE_UINT16( header->planes, 1 );
|
|
HB_PUT_LE_UINT16( header->bitsperpixel, pBMP->depth );
|
|
HB_PUT_LE_UINT32( header->compression, 0 );
|
|
HB_PUT_LE_UINT32( header->bitmapsize, bmpsize );
|
|
HB_PUT_LE_UINT32( header->hresolution, dpi );
|
|
HB_PUT_LE_UINT32( header->vresolution, dpi );
|
|
HB_PUT_LE_UINT32( header->clrused, clrused );
|
|
HB_PUT_LE_UINT32( header->clrimportant, 0 );
|
|
|
|
ptr = data + sizeof( HB_BMPHEADER );
|
|
for( idx = 0; idx < pBMP->clrused; ++idx )
|
|
{
|
|
*ptr++ = pBMP->palette[ idx ].blue;
|
|
*ptr++ = pBMP->palette[ idx ].green;
|
|
*ptr++ = pBMP->palette[ idx ].red;
|
|
*ptr++ = pBMP->palette[ idx ].alpha;
|
|
}
|
|
while( ptr < data + bmpoffset )
|
|
*ptr++ = 0;
|
|
memcpy( data + bmpoffset, pBMP->data, pBMP->rowlen * pBMP->height );
|
|
|
|
data[ file_size ] = 0;
|
|
*pnSize = file_size;
|
|
|
|
pBMP->error = 0;
|
|
|
|
return data;
|
|
}
|
|
|
|
/* Collectable pointer support */
|
|
|
|
static HB_GARBAGE_FUNC( hb_bmp_destructor )
|
|
{
|
|
PHB_BMPINFO * pBMPptr = ( PHB_BMPINFO * ) Cargo;
|
|
|
|
if( *pBMPptr )
|
|
{
|
|
hb_bmp_free( *pBMPptr );
|
|
*pBMPptr = NULL;
|
|
}
|
|
}
|
|
|
|
static const HB_GC_FUNCS s_gcBMPfuncs =
|
|
{
|
|
hb_bmp_destructor,
|
|
hb_gcDummyMark
|
|
};
|
|
|
|
void hb_bmpParamFree( int iParam )
|
|
{
|
|
PHB_BMPINFO * pBMPPtr = ( PHB_BMPINFO * ) hb_parptrGC( &s_gcBMPfuncs, iParam );
|
|
|
|
if( pBMPPtr && *pBMPPtr )
|
|
{
|
|
hb_bmp_free( *pBMPPtr );
|
|
*pBMPPtr = NULL;
|
|
}
|
|
}
|
|
|
|
PHB_BMPINFO hb_bmpParam( int iParam, HB_BOOL fError )
|
|
{
|
|
PHB_BMPINFO * pBMPPtr = ( PHB_BMPINFO * ) hb_parptrGC( &s_gcBMPfuncs, iParam );
|
|
|
|
if( pBMPPtr && *pBMPPtr )
|
|
return *pBMPPtr;
|
|
else if( fError )
|
|
hb_errRT_BASE_SubstR( EG_ARG, 3012, NULL, HB_ERR_FUNCNAME, HB_ERR_ARGS_BASEPARAMS );
|
|
return NULL;
|
|
}
|
|
|
|
void hb_bmpReturn( PHB_BMPINFO pBMP )
|
|
{
|
|
if( pBMP )
|
|
{
|
|
PHB_BMPINFO * pBMPPtr = ( PHB_BMPINFO * )
|
|
hb_gcAllocate( sizeof( PHB_BMPINFO ), &s_gcBMPfuncs );
|
|
*pBMPPtr = pBMP;
|
|
hb_retptrGC( pBMPPtr );
|
|
}
|
|
else
|
|
hb_ret();
|
|
}
|
|
|
|
/* PRG functions */
|
|
|
|
HB_FUNC( HB_BMP_NEW )
|
|
{
|
|
int iError = 0;
|
|
|
|
PHB_BMPINFO pBMP = hb_bmp_new( hb_parni( 1 ), hb_parni( 2 ),
|
|
hb_parnidef( 3, 1 ),
|
|
hb_parnidef( 4, HB_BMP_DPI_DEFAULT ),
|
|
&iError );
|
|
hb_storni( iError, 5 );
|
|
hb_bmpReturn( pBMP );
|
|
}
|
|
|
|
HB_FUNC( HB_BMP_COPY )
|
|
{
|
|
PHB_BMPINFO pBMP = hb_bmpParam( 1, HB_TRUE );
|
|
|
|
if( pBMP )
|
|
pBMP = hb_bmp_copy( pBMP );
|
|
|
|
hb_bmpReturn( pBMP );
|
|
}
|
|
|
|
HB_FUNC( HB_BMP_DECODE )
|
|
{
|
|
const char * data = hb_parc( 1 );
|
|
if( data )
|
|
{
|
|
int iError = 0;
|
|
PHB_BMPINFO pBMP = hb_bmp_decode( ( const HB_BYTE * ) data, hb_parclen( 1 ), &iError );
|
|
hb_storni( iError, 2 );
|
|
hb_bmpReturn( pBMP );
|
|
}
|
|
else
|
|
hb_errRT_BASE_SubstR( EG_ARG, 3012, NULL, HB_ERR_FUNCNAME, HB_ERR_ARGS_BASEPARAMS );
|
|
}
|
|
|
|
HB_FUNC( HB_BMP_ENCODE )
|
|
{
|
|
PHB_BMPINFO pBMP = hb_bmpParam( 1, HB_TRUE );
|
|
|
|
if( pBMP )
|
|
{
|
|
HB_SIZE size;
|
|
HB_BYTE * data = hb_bmp_encode( pBMP, &size );
|
|
|
|
hb_retclen_buffer( ( char * ) data, size );
|
|
}
|
|
}
|
|
|
|
HB_FUNC( HB_BMP_FREE )
|
|
{
|
|
hb_bmpParamFree( 1 );
|
|
}
|
|
|
|
HB_FUNC( HB_BMP_ERROR )
|
|
{
|
|
PHB_BMPINFO pBMP = hb_bmpParam( 1, HB_TRUE );
|
|
|
|
if( pBMP )
|
|
hb_retni( hb_bmp_error( pBMP ) );
|
|
}
|
|
|
|
HB_FUNC( HB_BMP_WIDTH )
|
|
{
|
|
PHB_BMPINFO pBMP = hb_bmpParam( 1, HB_TRUE );
|
|
|
|
if( pBMP )
|
|
hb_retni( hb_bmp_width( pBMP ) );
|
|
}
|
|
|
|
HB_FUNC( HB_BMP_HEIGHT )
|
|
{
|
|
PHB_BMPINFO pBMP = hb_bmpParam( 1, HB_TRUE );
|
|
|
|
if( pBMP )
|
|
hb_retni( hb_bmp_height( pBMP ) );
|
|
}
|
|
|
|
HB_FUNC( HB_BMP_DEPTH )
|
|
{
|
|
PHB_BMPINFO pBMP = hb_bmpParam( 1, HB_TRUE );
|
|
|
|
if( pBMP )
|
|
hb_retni( hb_bmp_depth( pBMP ) );
|
|
}
|
|
|
|
HB_FUNC( HB_BMP_COLOR )
|
|
{
|
|
PHB_BMPINFO pBMP = hb_bmpParam( 1, HB_TRUE );
|
|
|
|
if( pBMP )
|
|
hb_retnint( hb_bmp_color( pBMP, hb_parni( 2 ), hb_parni( 3 ), hb_parni( 4 ), hb_parni( 5 ) ) );
|
|
}
|
|
|
|
HB_FUNC( HB_BMP_COLOR2RGB )
|
|
{
|
|
PHB_BMPINFO pBMP = hb_bmpParam( 1, HB_TRUE );
|
|
|
|
if( pBMP )
|
|
{
|
|
int r, g, b, a;
|
|
hb_retl( hb_bmp_color2rgb( pBMP, hb_parnint( 2 ), &r, &g, &b, &a ) );
|
|
hb_storni( r, 3 );
|
|
hb_storni( g, 4 );
|
|
hb_storni( b, 5 );
|
|
hb_storni( a, 6 );
|
|
}
|
|
}
|
|
|
|
HB_FUNC( HB_BMP_PUTPIXEL )
|
|
{
|
|
PHB_BMPINFO pBMP = hb_bmpParam( 1, HB_TRUE );
|
|
|
|
if( pBMP )
|
|
hb_retl( hb_bmp_putpixel( pBMP, hb_parni( 2 ), hb_parni( 3 ), hb_parnint( 4 ) ) );
|
|
}
|
|
|
|
HB_FUNC( HB_BMP_GETPIXEL )
|
|
{
|
|
PHB_BMPINFO pBMP = hb_bmpParam( 1, HB_TRUE );
|
|
|
|
if( pBMP )
|
|
hb_retnint( hb_bmp_getpixel( pBMP, hb_parni( 2 ), hb_parni( 3 ) ) );
|
|
}
|
|
|
|
HB_FUNC( HB_BMP_LINE )
|
|
{
|
|
PHB_BMPINFO pBMP = hb_bmpParam( 1, HB_TRUE );
|
|
|
|
if( pBMP )
|
|
hb_bmp_line( pBMP, hb_parni( 2 ), hb_parni( 3 ), hb_parni( 4 ), hb_parni( 5 ), hb_parnint( 6 ) );
|
|
}
|
|
|
|
HB_FUNC( HB_BMP_RECT )
|
|
{
|
|
PHB_BMPINFO pBMP = hb_bmpParam( 1, HB_TRUE );
|
|
|
|
if( pBMP )
|
|
hb_bmp_rect( pBMP, hb_parni( 2 ), hb_parni( 3 ), hb_parni( 4 ), hb_parni( 5 ), hb_parnint( 6 ), hb_parldef( 7, HB_TRUE ) );
|
|
}
|
|
|
|
HB_FUNC( HB_BMP_LOAD )
|
|
{
|
|
const char * pszFileName = hb_parc( 1 );
|
|
|
|
if( pszFileName )
|
|
{
|
|
int iError = 0;
|
|
HB_SIZE size;
|
|
HB_BYTE * data = hb_fileLoad( pszFileName, 0x1000000, &size );
|
|
if( data )
|
|
{
|
|
PHB_BMPINFO pBMP = hb_bmp_decode( data, size, &iError );
|
|
hb_bmpReturn( pBMP );
|
|
hb_xfree( data );
|
|
}
|
|
else
|
|
iError = HB_BMP_ERROR_FILEREAD;
|
|
hb_storni( iError, 2 );
|
|
}
|
|
else
|
|
hb_errRT_BASE_SubstR( EG_ARG, 3012, NULL, HB_ERR_FUNCNAME, HB_ERR_ARGS_BASEPARAMS );
|
|
}
|
|
|
|
HB_FUNC( HB_BMP_SAVE )
|
|
{
|
|
PHB_BMPINFO pBMP = hb_bmpParam( 1, HB_TRUE );
|
|
|
|
if( pBMP )
|
|
{
|
|
const char * pszFileName = hb_parc( 2 );
|
|
|
|
if( pszFileName )
|
|
{
|
|
HB_SIZE size;
|
|
HB_BYTE * data = hb_bmp_encode( pBMP, &size );
|
|
HB_BOOL fResult = hb_fileSave( pszFileName, data, size );
|
|
if( ! fResult )
|
|
hb_bmp_seterror( pBMP, HB_BMP_ERROR_FILEWRITE );
|
|
hb_retl( fResult );
|
|
hb_xfree( data );
|
|
}
|
|
else
|
|
hb_errRT_BASE_SubstR( EG_ARG, 3012, NULL, HB_ERR_FUNCNAME, HB_ERR_ARGS_BASEPARAMS );
|
|
}
|
|
}
|