From 831983738796d0f49ee1af2774fba61c26d4569a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Przemys=C5=82aw=20Czerpak?= Date: Fri, 22 Aug 2025 21:50:57 +0200 Subject: [PATCH] 2025-08-22 21:50 UTC+0200 Przemyslaw Czerpak (druzus/at/poczta.onet.pl) + contrib/hbbmp/core.c + contrib/hbbmp/hbbmp.ch + contrib/hbbmp/hbbmp.h + contrib/hbbmp/hbbmp.hbc + contrib/hbbmp/hbbmp.hbp + contrib/hbbmp/hbbmp.hbx + contrib/hbbmp/readme.txt + contrib/hbbmp/tests/hbbmptst.prg + contrib/hbbmp/tests/hbmk.hbm + added new BMP image library for Harbour ; HBBMP is small library for Harbour dedicated to create and modify BMP images. It has not any 3rd party libs dependencies. See readme.txt for more information + contrib/hbzebra/tests/bmp.prg + added example code which uses HBBMP as backend for HBZEBRA and creates BMP images with generated barcodes. --- ChangeLog.txt | 19 + contrib/hbbmp/core.c | 876 +++++++++++++++++++++++++++++++ contrib/hbbmp/hbbmp.ch | 63 +++ contrib/hbbmp/hbbmp.h | 138 +++++ contrib/hbbmp/hbbmp.hbc | 5 + contrib/hbbmp/hbbmp.hbp | 10 + contrib/hbbmp/hbbmp.hbx | 46 ++ contrib/hbbmp/readme.txt | 110 ++++ contrib/hbbmp/tests/hbbmptst.prg | 75 +++ contrib/hbbmp/tests/hbmk.hbm | 3 + contrib/hbzebra/tests/bmp.prg | 106 ++++ 11 files changed, 1451 insertions(+) create mode 100644 contrib/hbbmp/core.c create mode 100644 contrib/hbbmp/hbbmp.ch create mode 100644 contrib/hbbmp/hbbmp.h create mode 100644 contrib/hbbmp/hbbmp.hbc create mode 100644 contrib/hbbmp/hbbmp.hbp create mode 100644 contrib/hbbmp/hbbmp.hbx create mode 100644 contrib/hbbmp/readme.txt create mode 100644 contrib/hbbmp/tests/hbbmptst.prg create mode 100644 contrib/hbbmp/tests/hbmk.hbm create mode 100644 contrib/hbzebra/tests/bmp.prg diff --git a/ChangeLog.txt b/ChangeLog.txt index b808041f21..f5313ec664 100644 --- a/ChangeLog.txt +++ b/ChangeLog.txt @@ -7,6 +7,25 @@ Entries may not always be in chronological/commit order. See license at the end of file. */ +2025-08-22 21:50 UTC+0200 Przemyslaw Czerpak (druzus/at/poczta.onet.pl) + + contrib/hbbmp/core.c + + contrib/hbbmp/hbbmp.ch + + contrib/hbbmp/hbbmp.h + + contrib/hbbmp/hbbmp.hbc + + contrib/hbbmp/hbbmp.hbp + + contrib/hbbmp/hbbmp.hbx + + contrib/hbbmp/readme.txt + + contrib/hbbmp/tests/hbbmptst.prg + + contrib/hbbmp/tests/hbmk.hbm + + added new BMP image library for Harbour + ; HBBMP is small library for Harbour dedicated to create and modify + BMP images. It has not any 3rd party libs dependencies. + See readme.txt for more information + + + contrib/hbzebra/tests/bmp.prg + + added example code which uses HBBMP as backend for HBZEBRA and + creates BMP images with generated barcodes. + 2025-08-22 12:00 UTC+0200 Przemyslaw Czerpak (druzus/at/poczta.onet.pl) * contrib/hbzebra/coredraw.c * contrib/hbzebra/hbzebra.hbx diff --git a/contrib/hbbmp/core.c b/contrib/hbbmp/core.c new file mode 100644 index 0000000000..3d297f19a6 --- /dev/null +++ b/contrib/hbbmp/core.c @@ -0,0 +1,876 @@ +/* + * BMP image library for Harbour + * + * Copyright 2025 Przemyslaw Czerpak + * + * 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( 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( 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 ); + } +} diff --git a/contrib/hbbmp/hbbmp.ch b/contrib/hbbmp/hbbmp.ch new file mode 100644 index 0000000000..65938041cc --- /dev/null +++ b/contrib/hbbmp/hbbmp.ch @@ -0,0 +1,63 @@ +/* + * BMP image library for Harbour + * + * Copyright 2025 Przemyslaw Czerpak + * + * 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. + * + */ + +/* NOTE: This file is also used by C code. */ + +#ifndef _HBBMP_CH_ +#define _HBBMP_CH_ + +#define HB_BMP_ERROR_SIZE 1 +#define HB_BMP_ERROR_DEPTH 2 +#define HB_BMP_ERROR_COLORVALUE 3 +#define HB_BMP_ERROR_PALETTEFULL 4 +#define HB_BMP_ERROR_COLORINDEX 5 +#define HB_BMP_ERROR_RANGE 6 +#define HB_BMP_ERROR_CORRUPT 7 +#define HB_BMP_ERROR_UNSUPPORTED 8 +#define HB_BMP_ERROR_FILEREAD 9 +#define HB_BMP_ERROR_FILEWRITE 10 + +#endif /* _HBBMP_CH_ */ diff --git a/contrib/hbbmp/hbbmp.h b/contrib/hbbmp/hbbmp.h new file mode 100644 index 0000000000..8891ea2b17 --- /dev/null +++ b/contrib/hbbmp/hbbmp.h @@ -0,0 +1,138 @@ +/* + * BMP image library for Harbour + * + * Copyright 2025 Przemyslaw Czerpak + * + * 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. + * + */ + +#ifndef _HBBMP_H_ +#define _HBBMP_H_ + +#include "hbdefs.h" +#include "hbbmp.ch" + +HB_EXTERN_BEGIN + +#define HB_BMP_DEPTH_DEFAULT 1 +#define HB_BMP_DPI_DEFAULT 72 +#define HB_BMP_INCHES_PER_METRE 39.3701 + +#define HB_BMP_FILEHEADER_SIZE 14 +#define HB_BMP_INFOHEADER_MINSIZE 12 + +typedef struct _HB_BMPHEADER +{ + HB_BYTE signature [ 2 ]; /* "BM" */ + HB_BYTE file_size [ 4 ]; /* the size of the BMP file in bytes */ + HB_BYTE reserved1 [ 2 ]; /* reserved, depends on the application, if created manually can be 0 */ + HB_BYTE reserved2 [ 2 ]; /* reserved, depends on the application, if created manually can be 0 */ + HB_BYTE bmpoffset [ 4 ]; /* the offset of the byte where the bitmap image data (pixel array) can be found */ + + HB_BYTE headersize [ 4 ]; /* the size of this header, in bytes (40) */ + HB_BYTE width [ 4 ]; /* the bitmap width in pixels (signed integer) */ + HB_BYTE height [ 4 ]; /* the bitmap height in pixels (signed integer) */ + HB_BYTE planes [ 2 ]; /* the number of color planes (must be 1) */ + HB_BYTE bitsperpixel[ 2 ]; /* the number of bits per pixel (color depth): 1, 4, 8, 16, 24 or 32 */ + HB_BYTE compression [ 4 ]; /* the compression method, 0 uncompressed */ + HB_BYTE bitmapsize [ 4 ]; /* the image size (the size of the raw bitmap data), 0 can be used for uncompressed bitmaps */ + HB_BYTE hresolution [ 4 ]; /* the horizontal resolution of the image (pixel per metre, signed integer) */ + HB_BYTE vresolution [ 4 ]; /* the vertical resolution of the image (pixel per metre, signed integer) */ + HB_BYTE clrused [ 4 ]; /* the number of colors in the color palette, or 0 to default to 2^n */ + HB_BYTE clrimportant[ 4 ]; /* the number of important colors used, or 0 when every color is important; generally ignored */ +} HB_BMPHEADER, * PHB_BMPHEADER; + +typedef struct _HB_BMPOS2HEADER +{ + HB_BYTE headersize [ 4 ]; /* the size of this header, in bytes (12) */ + HB_BYTE width [ 2 ]; /* the bitmap width in pixels (signed integer) */ + HB_BYTE height [ 2 ]; /* the bitmap height in pixels (signed integer) */ + HB_BYTE planes [ 2 ]; /* the number of color planes (must be 1) */ + HB_BYTE bitsperpixel[ 2 ]; /* the number of bits per pixel (color depth): 1, 4, 8, 16, 24 or 32 */ +} HB_BMPOS2HEADER, * PHB_BMPOS2HEADER; + +typedef struct _HB_BMPPALETTEITM +{ + HB_BYTE blue; + HB_BYTE green; + HB_BYTE red; + HB_BYTE alpha; +} HB_BMPPALETTEITM, * PHB_BMPPALETTEITM; + +typedef struct _HB_BMPINFO +{ + int error; + int width; + int height; + int rowlen; + HB_BOOL fromtop; + int dpi; + int depth; + int clrused; + HB_BMPPALETTEITM palette[ 256 ]; + HB_BYTE* data; +} HB_BMPINFO, * PHB_BMPINFO; + +extern HB_EXPORT PHB_BMPINFO hb_bmp_new( int width, int height, int depth, int dpi, int * piError ); +extern HB_EXPORT PHB_BMPINFO hb_bmp_copy( PHB_BMPINFO pBMP ); +extern HB_EXPORT PHB_BMPINFO hb_bmp_decode( const HB_BYTE * data, HB_SIZE size, int * piError ); +extern HB_EXPORT HB_BYTE * hb_bmp_encode( PHB_BMPINFO pBMP, HB_SIZE * pnSsize ); +extern HB_EXPORT void hb_bmp_free( PHB_BMPINFO pBMP ); +extern HB_EXPORT HB_BYTE * hb_bmp_bitmapptr( PHB_BMPINFO pBMP, HB_SIZE * pnSize ); +extern HB_EXPORT void hb_bmp_seterror( PHB_BMPINFO pBMP, int error ); +extern HB_EXPORT int hb_bmp_error( PHB_BMPINFO pBMP ); +extern HB_EXPORT int hb_bmp_width( PHB_BMPINFO pBMP ); +extern HB_EXPORT int hb_bmp_height( PHB_BMPINFO pBMP ); +extern HB_EXPORT int hb_bmp_depth( PHB_BMPINFO pBMP ); +extern HB_EXPORT HB_MAXINT hb_bmp_color( PHB_BMPINFO pBMP, int r, int g, int b, int a ); +extern HB_EXPORT HB_BOOL hb_bmp_color2rgb( PHB_BMPINFO pBMP, HB_MAXINT clr, int * r, int * g, int * b, int * a ); +extern HB_EXPORT HB_BOOL hb_bmp_putpixel( PHB_BMPINFO pBMP, int x, int y, HB_MAXINT clr ); +extern HB_EXPORT HB_MAXINT hb_bmp_getpixel( PHB_BMPINFO pBMP, int x, int y ); +extern HB_EXPORT void hb_bmp_line( PHB_BMPINFO pBMP, int x1, int y1, int x2, int y2, HB_MAXINT clr ); +extern HB_EXPORT void hb_bmp_rect( PHB_BMPINFO pBMP, int x, int y, int width, int height, HB_MAXINT clr, HB_BOOL fFill ); + +extern HB_EXPORT PHB_BMPINFO hb_bmpParam( int iParam, HB_BOOL fError ); +extern HB_EXPORT void hb_bmpParamFree( int iParam ); +extern HB_EXPORT void hb_bmpReturn( PHB_BMPINFO pBMP ); + +HB_EXTERN_END + +#endif /* _HBBMP_H_ */ diff --git a/contrib/hbbmp/hbbmp.hbc b/contrib/hbbmp/hbbmp.hbc new file mode 100644 index 0000000000..14249ef90e --- /dev/null +++ b/contrib/hbbmp/hbbmp.hbc @@ -0,0 +1,5 @@ +description=BMP image library for Harbour + +incpaths=. + +libs=${hb_name} diff --git a/contrib/hbbmp/hbbmp.hbp b/contrib/hbbmp/hbbmp.hbp new file mode 100644 index 0000000000..d2a5933618 --- /dev/null +++ b/contrib/hbbmp/hbbmp.hbp @@ -0,0 +1,10 @@ +-hblib +-inc + +-o${hb_name} + +-w3 -es2 + +${hb_name}.hbx + +core.c diff --git a/contrib/hbbmp/hbbmp.hbx b/contrib/hbbmp/hbbmp.hbx new file mode 100644 index 0000000000..2327f83f7a --- /dev/null +++ b/contrib/hbbmp/hbbmp.hbx @@ -0,0 +1,46 @@ +/* -------------------------------------------------------------------- + * NOTE: You can add manual override which functions to include or + * exclude from automatically generated EXTERNAL/DYNAMIC list. + * Syntax: // HB_FUNC_INCLUDE + * // HB_FUNC_EXCLUDE + */ + +/* -------------------------------------------------------------------- + * WARNING: Automatically generated code below. DO NOT EDIT! (except casing) + * Regenerate using hbmk2 '-hbx=' option. + */ + +#ifndef __HBEXTERN_CH__HBBMP__ +#define __HBEXTERN_CH__HBBMP__ + +#if defined( __HBEXTREQ__ ) .OR. defined( __HBEXTERN__HBBMP__ANNOUNCE ) + ANNOUNCE __HBEXTERN__HBBMP__ +#endif + +#if defined( __HBEXTREQ__ ) .OR. defined( __HBEXTERN__HBBMP__REQUEST ) + #command DYNAMIC => EXTERNAL +#endif + +DYNAMIC hb_bmp_color +DYNAMIC hb_bmp_color2rgb +DYNAMIC hb_bmp_copy +DYNAMIC hb_bmp_decode +DYNAMIC hb_bmp_depth +DYNAMIC hb_bmp_encode +DYNAMIC HB_BMP_ERROR +DYNAMIC hb_bmp_free +DYNAMIC hb_bmp_getpixel +DYNAMIC hb_bmp_height +DYNAMIC hb_bmp_line +DYNAMIC hb_bmp_load +DYNAMIC hb_bmp_new +DYNAMIC hb_bmp_putpixel +DYNAMIC hb_bmp_rect +DYNAMIC hb_bmp_save +DYNAMIC hb_bmp_width + +#if defined( __HBEXTREQ__ ) .OR. defined( __HBEXTERN__HBBMP__REQUEST ) + #uncommand DYNAMIC => EXTERNAL +#endif + +#endif diff --git a/contrib/hbbmp/readme.txt b/contrib/hbbmp/readme.txt new file mode 100644 index 0000000000..d42b4de275 --- /dev/null +++ b/contrib/hbbmp/readme.txt @@ -0,0 +1,110 @@ +Copyright 2025 Przemyslaw Czerpak + +HBBMP is small library for Harbour dedicated to create and modify +BMP images. It has not any 3rd party libs dependencies. +It allows to create new images load existing ones and draw +simple graphics primitives. I did not implement more advanced +ones to not create math lib dependencies. +Anyhow if someone is interesting in it then please implement it. +For me this library is enough to work as backend for HBZEBRA +library (excellent job of Mindaugas Kavaliauskas). +I hope you will find it useful. + +Only uncompressed BMP images are supported. When BMP file with +compression flag is loaded then UNSUPPORTED error is set. This +is the only case when this error can appear. + +This library can work images using 1, 4, 8, 16, 24 and 32 color +depth (all officially defined). It can work with 2-bit color depth +(4 colors) BMP images which are not supported in official +documentation. I blocked them inside hb_bmp_new() C function but +if someone needs to create such BMP file then it's enough to +uncomment one line with "case 2:" in this code. +In BMP images with 1, 4 and 8 color depths number of colors is +limited to 2, 16 and 256 colors. If limit exceeds or unsupported +colors are defined then -1 is returned and error is set. +In BMP images with 24 color depth alpha channel does not exists +and this library silently ignores it when user pass it in color +index. +BMP images with 16-bit color depth does not have RGB color table +so only arbitrary color indexes can be used and their real colors +necessary for visualization have to be defined outside BMP file. +BMP images color depth 24 and 32 do not use color table and color +indexes are pure big endian RGB values with optional alpha channel +which is ignored in case of 24 bit depth (0xAARRGGBB) so it's not +necessary to allocate colors and programmer may use it directly, +i.e. red := 0xFF0000 +The color and alpha must be in range from 0 to 255. +When new BMP is created user can change the order in which rows +are stored in memory and files from bottom-up to top-down setting +the image height to negative value. + +On PRG level the BMP image structure is represented as pointer +item with attached destructor so when last variable (Harbour +item) pointing to given BMP structure is cleared or overwritten +(f.e. by pBMP := NIL) then destructor is called and BMP image +structure removed from memory. Anyhow it's possible to force +release operation by explicit call to hb_bmp_free(). + + + +The following PRG functions exist in HBBMP lib: + +Create new BMP in memory, return pointer item or NIL on error. + hb_bmp_new( , , [=1], [=72], [@] ) + => | NIL (error indicator) + +Make independent copy of memory BMP structures: + hb_bmp_copy( ) => + +Free BMP structure from memory: + hb_bmp_free( ) => NIL + +Load BMP image from file, return pointer item BMP image structure +or NIL on error: + hb_bmp_load( , [@] ) => | NIL + +Save BMP image into file: + hb_bmp_save( , ) => + +Create new BMP image structure from passed BMP file body, return +pointer item BMP structure or NIL on error: + hb_bmp_decode( , [@] ) => | NIL + +Create BMP file body from BMP image structure: + hb_bmp_encode( ) => + +Get BMP width: + hb_bmp_width( ) => + +Get BMP height: + hb_bmp_height( ) => + +Get BMP color depth: + hb_bmp_depth( ) => + +Allocate new color or take index to existing one, or error -1 +is returned: + hb_bmp_color( , , , , ) + => | -1 + +Convert color index to its RGB value, return .T. on success or +.F. in case of error: + hb_bmp_color2rgb( , , + @, @, @, @ ) + => + +Set pixel using given coordinates and color index: + hb_bmp_putpixel( , , , ) => + +Get color index of pixel at given coordinates, on error -1 +is returned: + hb_bmp_getpixel( , , ) => + +Draw line from the given starting and ending points and color index: + hb_bmp_line( , , , , , ) => NIL + +Draw rectangle (filled by default) using given coordinates, size and +index color: + hb_bmp_rect( , , , , , + , [=.t.] ) => NIL diff --git a/contrib/hbbmp/tests/hbbmptst.prg b/contrib/hbbmp/tests/hbbmptst.prg new file mode 100644 index 0000000000..9732ba728d --- /dev/null +++ b/contrib/hbbmp/tests/hbbmptst.prg @@ -0,0 +1,75 @@ +/* + * Copyright 2025 Przemyslaw Czerpak + */ + +#define WIDTH 240 +#define HEIGHT 200 + +#xtranslate _out( [, ] ) => outstd( hb_strFormat( , ) + hb_eol() ) +#xtranslate _err( [, ] ) => outerr( hb_strFormat( , ) + hb_eol() ) + +REQUEST HB_GT_CGI_DEFAULT + +PROCEDURE Main() +LOCAL pBMP, depth, bkg, black, red, green, blue, last, fname, x1, x2, y1, y2, nn + + FOR EACH depth IN { 1, 4, 8, 24, 32 } + IF Empty( pBMP := hb_bmp_new( WIDTH, HEIGHT, depth ) ) + _err( "Cannot create BMP with DEPTH=%d", depth ) + ELSE + _out( "Created BMP width=%d, height=%d, depth=%d", ; + hb_bmp_width( pBMP ), hb_bmp_height( pBMP ), hb_bmp_depth( pBMP ) ) + bmp_color( pBMP, 255, 255, 255, 0, @bkg, @last ) + bmp_color( pBMP, 0, 0, 0, 0, @black, @last ) + bmp_color( pBMP, 127, 0, 0, 0, @red, @last ) + bmp_color( pBMP, 0, 127, 0, 0, @green, @last ) + bmp_color( pBMP, 0, 0, 127, 0, @blue, @last ) + + hb_bmp_rect( pBMP, 0, 0, WIDTH, HEIGHT, bkg ) + hb_bmp_rect( pBMP, 1, 1, WIDTH - 2, HEIGHT - 2, black, .f. ) + + hb_bmp_rect( pBMP, 20, 20, WIDTH - 40, HEIGHT - 40, black ) + hb_bmp_rect( pBMP, 30, 30, WIDTH - 60, HEIGHT - 60, bkg ) + x1 := 40 ; y1 := 40 ; x2 := WIDTH - 40 ; y2 := HEIGHT - 40 + hb_bmp_rect( pBMP, x1, y1, x2 - x1 + 1, y2 - y1 + 1, red, .f. ) + hb_bmp_line( pBMP, x1, y1, x2, y2, blue ) + hb_bmp_line( pBMP, x2, y1, x1, y2, green ) + hb_bmp_line( pBMP, x1 + x1, y1, x2 - x1, y2, blue ) + hb_bmp_line( pBMP, x2 - x1, y1, x1 + x1, y2, green ) + + FOR nn := 1 TO 5 + hb_bmp_rect( pBMP, 20 * nn, 8, 12, 8, { green, blue, red, black, green }[ nn ] ) + NEXT + + fname := "test_" + strzero( depth, 2 ) + ".bmp" + bmp_save( pBMP, fname ) + pBMP := NIL + ENDIF + _out( "" ) + NEXT +RETURN + +STATIC FUNCTION bmp_color( pBMP, r, g, b, a, /*@*/clr, /*@*/last ) +LOCAL r2, g2, b2, a2 + clr = hb_bmp_color( pBMP, r, g, b, a ) + IF clr >= 0 + last := clr + hb_bmp_color2rgb( pBMP, clr, @r2, @g2, @b2, @a2 ) + _out( "Allocated color 0x%08x: r=%s, g=%s, b=%s, a=%s => r=%s, g=%s, b=%s, a=%s", clr, ; + hb_valToExp( r ), hb_valToExp( g ), hb_valToExp( b ), hb_valToExp( a ), ; + hb_valToExp( r2 ), hb_valToExp( g2 ), hb_valToExp( b2 ), hb_valToExp( a2 ) ) + RETURN .T. + ELSE + clr := last + _err( "Cannot allocate color" ) + ENDIF +RETURN .F. + +STATIC FUNCTION bmp_save( pBMP, fname ) + IF hb_bmp_save( pBMP, fname ) + _out( "Created file: %s", fname ) + RETURN .T. + ELSE + _err( "Cannot save file: %s", fname ) + ENDIF +RETURN .F. diff --git a/contrib/hbbmp/tests/hbmk.hbm b/contrib/hbbmp/tests/hbmk.hbm new file mode 100644 index 0000000000..204ee7899d --- /dev/null +++ b/contrib/hbbmp/tests/hbmk.hbm @@ -0,0 +1,3 @@ +hbbmp.hbc + +-w3 -es2 diff --git a/contrib/hbzebra/tests/bmp.prg b/contrib/hbzebra/tests/bmp.prg new file mode 100644 index 0000000000..656de86b13 --- /dev/null +++ b/contrib/hbzebra/tests/bmp.prg @@ -0,0 +1,106 @@ +/* + * Copyright 2025 Przemyslaw Czerpak + */ + +#require "hbzebra" +#require "hbbmp" + +#define WIDTH 1 + +REQUEST HB_GT_CGI_DEFAULT + +PROCEDURE Main() + + DrawBarcode( WIDTH, "EAN13", "477012345678" ) + DrawBarcode( WIDTH, "EAN8", "1234567" ) + DrawBarcode( WIDTH, "UPCA", "01234567891" ) + DrawBarcode( WIDTH, "UPCE", "123456" ) + DrawBarcode( WIDTH, "CODE39", "ABC123" ) + DrawBarcode( WIDTH, "CODE39", "ABC123", HB_ZEBRA_FLAG_CHECKSUM + HB_ZEBRA_FLAG_WIDE3 ) + DrawBarcode( WIDTH, "ITF", "12345678901", HB_ZEBRA_FLAG_CHECKSUM ) + DrawBarcode( WIDTH, "MSI", "1234567", HB_ZEBRA_FLAG_CHECKSUM ) + DrawBarcode( WIDTH, "CODABAR", "40156", HB_ZEBRA_FLAG_WIDE3 ) + DrawBarcode( WIDTH, "CODE93", "ABC-123" ) + DrawBarcode( WIDTH, "CODE11", "1234567890", HB_ZEBRA_FLAG_CHECKSUM + HB_ZEBRA_FLAG_WIDE3 ) + DrawBarcode( WIDTH, "CODE128", "Code 128" ) + DrawBarcode( WIDTH, "PDF417", "Hello, World of Harbour!!! It's 2D barcode PDF417 :)" ) + DrawBarcode( WIDTH, "DATAMATRIX", "Hello, World of Harbour!!! It's 2D barcode DataMatrix :)" ) + DrawBarcode( WIDTH, "QRCODE", "https://en.wikipedia.org/wiki/QR_Code" ) + + ? + + RETURN + +STATIC PROCEDURE DrawBarcode( nLineWidth, cType, cCode, nFlags ) + + LOCAL hZebra, pBMP, nLineHeight, nX, nY, nWidth, nHeight, nDepth, nColor, cFile + + SWITCH cType + CASE "EAN13" ; hZebra := hb_zebra_create_ean13( cCode, nFlags ) ; EXIT + CASE "EAN8" ; hZebra := hb_zebra_create_ean8( cCode, nFlags ) ; EXIT + CASE "UPCA" ; hZebra := hb_zebra_create_upca( cCode, nFlags ) ; EXIT + CASE "UPCE" ; hZebra := hb_zebra_create_upce( cCode, nFlags ) ; EXIT + CASE "CODE39" ; hZebra := hb_zebra_create_code39( cCode, nFlags ) ; EXIT + CASE "ITF" ; hZebra := hb_zebra_create_itf( cCode, nFlags ) ; EXIT + CASE "MSI" ; hZebra := hb_zebra_create_msi( cCode, nFlags ) ; EXIT + CASE "CODABAR" ; hZebra := hb_zebra_create_codabar( cCode, nFlags ) ; EXIT + CASE "CODE93" ; hZebra := hb_zebra_create_code93( cCode, nFlags ) ; EXIT + CASE "CODE11" ; hZebra := hb_zebra_create_code11( cCode, nFlags ) ; EXIT + CASE "CODE128" ; hZebra := hb_zebra_create_code128( cCode, nFlags ) ; EXIT + CASE "PDF417" ; hZebra := hb_zebra_create_pdf417( cCode, nFlags ); nLineHeight := nLineWidth * 3 ; EXIT + CASE "DATAMATRIX" ; hZebra := hb_zebra_create_datamatrix( cCode, nFlags ); nLineHeight := nLineWidth ; EXIT + CASE "QRCODE" ; hZebra := hb_zebra_create_qrcode( cCode, nFlags ); nLineHeight := nLineWidth ; EXIT + ENDSWITCH + + IF hZebra != NIL + IF hb_zebra_geterror( hZebra ) == 0 + + IF nLineHeight == NIL + nLineHeight := nLineWidth * 36 + ENDIF + + nDepth := 1 + /* get barcode size and add 1 pixel border over it */ + hb_zebra_getsize( hZebra, @nWidth, @nHeight ) + nX := 1 + nY := 1 + nWidth := nWidth * nLineWidth + 2 + nHeight := nHeight * nLineHeight + 2 + + ? cType, "code width", hb_ntos( nWidth ), "height", hb_ntos( nHeight ) + IF Empty( pBMP := hb_bmp_new( nWidth, nHeight, nDepth ) ) + ? "Cannot create BMP image" + ELSE + /* allocate white color (RGB) and use it to fill the background */ + nColor := hb_bmp_color( pBMP, 255, 255, 255 ) + hb_bmp_rect( pBMP, 0, 0, nWidth, nHeight, nColor ) + /* allocate black color for barcode drawing */ + nColor := hb_bmp_color( pBMP, 0, 0, 0 ) + ? "Building BMP with", cType, "code for vale:", hb_zebra_getcode( hZebra ) + hb_zebra_draw_bmp( hZebra, pBMP, nColor, nX, nY, nLineWidth, nLineHeight ) + cFile := Lower( cType ) + ".bmp" + ? "Creating BMP file:", cFile + IF ! hb_bmp_save( pBMP, cFile ) + ? "Cannot save BMP to file:", cFile + ENDIF + /* destroy BMP file */ + pBMP := NIL + ENDIF + ELSE + ? "Type", cType, "Code", cCode, "Error", hb_zebra_geterror( hZebra ) + ENDIF + hb_zebra_destroy( hZebra ) + ELSE + ? "Invalid barcode type", cType + ENDIF + ? + + RETURN + +STATIC FUNCTION hb_zebra_draw_bmp( hZebra, pBMP, nColor, ... ) + + IF hb_zebra_geterror( hZebra ) != 0 + RETURN HB_ZEBRA_ERROR_INVALIDZEBRA + ENDIF + + RETURN hb_zebra_draw( hZebra, {| x, y, w, h | hb_bmp_rect( pBMP, x, y, w, h, nColor ) }, ... )