From 68bc56f864e3e1acd00870ca04085f387b144494 Mon Sep 17 00:00:00 2001 From: Werner Lemberg Date: Sat, 16 Jun 2018 09:39:29 +0200 Subject: [PATCH] Add `FT_Bitmap_Blend' API. Still missing: Support for negative bitmap pitch and subpixel offset of source bitmap. * include/freetype/ftbitmap.h, src/base/ftbitmap.c (FT_Bitmap_Blend): New function. --- ChangeLog | 10 ++ include/freetype/ftbitmap.h | 59 +++++++ src/base/ftbitmap.c | 296 +++++++++++++++++++++++++++++++++++- 3 files changed, 364 insertions(+), 1 deletion(-) diff --git a/ChangeLog b/ChangeLog index d1559ac2e..00def3157 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,13 @@ +2018-06-16 Werner Lemberg + + Add `FT_Bitmap_Blend' API. + + Still missing: Support for negative bitmap pitch and subpixel offset + of source bitmap. + + * include/freetype/ftbitmap.h, src/base/ftbitmap.c + (FT_Bitmap_Blend): New function. + 2018-06-14 Werner Lemberg Replace `FT_Get_GlyphLayers' with `FT_Get_Color_Glyph_Layer'. diff --git a/include/freetype/ftbitmap.h b/include/freetype/ftbitmap.h index 809e3bd11..c2314b332 100644 --- a/include/freetype/ftbitmap.h +++ b/include/freetype/ftbitmap.h @@ -22,6 +22,7 @@ #include #include FT_FREETYPE_H +#include FT_COLOR_H #ifdef FREETYPE_H #error "freetype.h of FreeType 1 has been loaded!" @@ -194,6 +195,64 @@ FT_BEGIN_HEADER FT_Int alignment ); + /************************************************************************** + * + * @Function: + * FT_Bitmap_Blend + * + * @Description: + * Blend a bitmap onto another bitmap, using a given color. + * + * @Input: + * library :: + * A handle to a library object. + * + * source :: + * The source bitmap, which can have any @FT_Pixel_Mode format. + * + * source_offset :: + * The offset vector to the upper left corner of the source bitmap in + * 26.6 pixel format. This can be a fractional pixel value. + * + * color :: + * The color used to draw `source' onto `target'. + * + * @InOut: + * target :: + * A handle to an `FT_Bitmap' object. It should be either initialized + * as empty with a call to @FT_Bitmap_Init, or it should be of type + * @FT_PIXEL_MODE_BGRA. + * + * atarget_offset :: + * The offset vector to the upper left corner of the target bitmap in + * 26.6 pixel format. It should represent an integer offset; the + * function will set the lowest six bits to zero to enforce that. + * + * @Return: + * FreeType error code. 0~means success. + * + * @Note: + * This function doesn't perform clipping. + * + * The bitmap in `target' gets allocated or reallocated as needed; the + * vector `atarget_offset' is updated accordingly. + * + * In case of allocation or reallocation, the bitmap's pitch is set to + * `4~*~width'. Both `source' and `target' must have the same bitmap + * flow (as indicated by the sign of the `pitch' field). + * + * @Since: + * 2.10 + */ + FT_EXPORT( FT_Error ) + FT_Bitmap_Blend( FT_Library library, + const FT_Bitmap* source, + const FT_Vector source_offset, + FT_Bitmap* target, + FT_Vector *atarget_offset, + FT_Color color ); + + /************************************************************************** * * @Function: diff --git a/src/base/ftbitmap.c b/src/base/ftbitmap.c index 68647f86e..e848e66a3 100644 --- a/src/base/ftbitmap.c +++ b/src/base/ftbitmap.c @@ -25,7 +25,7 @@ static - const FT_Bitmap null_bitmap = { 0, 0, 0, 0, 0, 0, 0, 0 }; + const FT_Bitmap null_bitmap = { 0, 0, 0, NULL, 0, 0, 0, NULL }; /* documentation is in ftbitmap.h */ @@ -783,6 +783,300 @@ } + /* documentation is in ftbitmap.h */ + + FT_EXPORT_DEF( FT_Error ) + FT_Bitmap_Blend( FT_Library library, + const FT_Bitmap* source_, + const FT_Vector source_offset_, + FT_Bitmap* target, + FT_Vector *atarget_offset, + FT_Color color ) + { + FT_Error error = FT_Err_Ok; + FT_Memory memory; + + FT_Bitmap source_bitmap; + const FT_Bitmap* source; + + FT_Vector source_offset; + FT_Vector target_offset; + FT_Vector frac_offset; + + FT_Bool free_source_bitmap = 0; + FT_Bool free_target_bitmap_on_error = 0; + + FT_Pos source_llx, source_lly, source_urx, source_ury; + FT_Pos target_llx, target_lly, target_urx, target_ury; + FT_Pos final_llx, final_lly, final_urx, final_ury; + + unsigned int final_rows, final_width; + long x, y; + + + if ( !library || !target || !source_ || !atarget_offset ) + return FT_THROW( Invalid_Argument ); + + memory = library->memory; + + if ( !( target->pixel_mode == FT_PIXEL_MODE_NONE || + ( target->pixel_mode == FT_PIXEL_MODE_BGRA && + target->buffer ) ) ) + return FT_THROW( Invalid_Argument ); + + if ( source_->pixel_mode == FT_PIXEL_MODE_NONE ) + return FT_Err_Ok; /* nothing to do */ + + /* pitches must have the same sign */ + if ( target->pixel_mode == FT_PIXEL_MODE_BGRA && + ( source_->pitch ^ target->pitch ) < 0 ) + return FT_THROW( Invalid_Argument ); + + if ( !( source_->width && source_->rows ) ) + return FT_Err_Ok; /* nothing to do */ + + /* we isolate a fractional shift of `source', */ + /* to be less than one pixel and always positive; */ + /* `source_offset' now holds full-pixel shift values */ + source_offset.x = FT_PIX_FLOOR( source_offset_.x ); + frac_offset.x = source_offset_.x - source_offset.x; + + source_offset.y = FT_PIX_FLOOR( source_offset_.y ); + frac_offset.y = source_offset_.y - source_offset.y; + + /* assure integer pixel offset for target bitmap */ + target_offset.x = FT_PIX_FLOOR( atarget_offset->x ); + target_offset.y = FT_PIX_FLOOR( atarget_offset->y ); + + /* get source bitmap dimensions */ + source_llx = source_offset.x; + if ( FT_LONG_MIN + ( source_->rows << 6 ) + 64 > source_offset.y ) + return FT_THROW( Invalid_Argument ); + source_lly = source_offset.y - ( source_->rows << 6 ); + + if ( FT_LONG_MAX - ( source_->width << 6 ) - 64 < source_llx ) + return FT_THROW( Invalid_Argument ); + source_urx = source_llx + ( source_->width << 6 ); + source_ury = source_offset.y; + + /* get target bitmap dimensions */ + if ( target->width && target->rows ) + { + target_llx = target_offset.x; + if ( FT_LONG_MIN + ( target->rows << 6 ) > target_offset.y ) + return FT_THROW( Invalid_Argument ); + target_lly = target_offset.y - ( target->rows << 6 ); + + if ( FT_LONG_MAX - ( target->width << 6 ) < target_llx ) + return FT_THROW( Invalid_Argument ); + target_urx = target_llx + ( target->width << 6 ); + target_ury = target_offset.y; + } + else + { + target_llx = FT_LONG_MAX; + target_lly = FT_LONG_MAX; + target_urx = FT_LONG_MIN; + target_ury = FT_LONG_MIN; + } + + /* move upper right corner up and to the right */ + /* if we have a fractional offset */ + if ( source_urx >= target_urx && frac_offset.x ) + source_urx += 64; + if ( source_ury >= target_ury && frac_offset.y ) + source_ury += 64; + + /* compute final bitmap dimensions */ + final_llx = FT_MIN( source_llx, target_llx ); + final_lly = FT_MIN( source_lly, target_lly ); + final_urx = FT_MAX( source_urx, target_urx ); + final_ury = FT_MAX( source_ury, target_ury ); + + final_width = ( final_urx - final_llx ) >> 6; + final_rows = ( final_ury - final_lly ) >> 6; + + /* for blending, set offset vector of final bitmap */ + /* temporarily to (0,0) */ + source_llx -= final_llx; + source_lly -= final_lly; + + target_llx -= final_llx; + target_lly -= final_lly; + + /* set up target bitmap */ + if ( target->pixel_mode == FT_PIXEL_MODE_NONE ) + { + /* create new empty bitmap */ + target->width = final_width; + target->rows = final_rows; + target->pixel_mode = FT_PIXEL_MODE_BGRA; + target->pitch = (int)final_width * 4; + target->num_grays = 256; + + if ( FT_LONG_MAX / target->pitch < target->rows ) + return FT_THROW( Invalid_Argument ); + + if ( FT_ALLOC( target->buffer, target->pitch * (int)target->rows ) ) + return error; + + free_target_bitmap_on_error = 1; + } + else if ( target->width != final_width || + target->rows != final_rows ) + { + /* adjust old bitmap to enlarged size */ + int pitch, new_pitch; + + unsigned char* buffer = NULL; + + + pitch = target->pitch; + if ( pitch < 0 ) + pitch = -pitch; + + new_pitch = (int)final_width * 4; + + if ( FT_LONG_MAX / new_pitch < final_rows ) + return FT_THROW( Invalid_Argument ); + + /* TODO: provide an in-buffer solution for large bitmaps */ + /* to avoid allocation of a new buffer */ + if ( FT_ALLOC( buffer, new_pitch * (int)final_rows ) ) + goto Error; + + /* copy data to new buffer */ + x = target_llx >> 6; + y = target_lly >> 6; + + /* the bitmap flow is from top to bottom, */ + /* but y is measured from bottom to top */ + if ( target->pitch < 0 ) + { + /* XXX */ + } + else + { + unsigned char* p = + target->buffer; + unsigned char* q = + buffer + + ( final_rows - y - target->rows ) * new_pitch + + x * 4; + unsigned char* limit_p = + p + pitch * (int)target->rows; + + + while ( p < limit_p ) + { + FT_MEM_COPY( q, p, pitch ); + + p += pitch; + q += new_pitch; + } + } + + FT_FREE( target->buffer ); + + target->width = final_width; + target->rows = final_rows; + + if ( target->pitch < 0 ) + target->pitch = -new_pitch; + else + target->pitch = new_pitch; + + target->buffer = buffer; + } + + /* adjust source bitmap if necessary */ + if ( source_->pixel_mode != FT_PIXEL_MODE_GRAY ) + { + FT_Bitmap_Init( &source_bitmap ); + error = FT_Bitmap_Convert( library, source_, &source_bitmap, 1 ); + if ( error ) + goto Error; + + source = &source_bitmap; + free_source_bitmap = 1; + } + else + source = source_; + + /* do blending; the code below returns pre-multiplied channels, */ + /* similar to what FreeType gets from `CBDT' tables */ + x = source_llx >> 6; + y = source_lly >> 6; + + /* XXX handle `frac_offset' */ + + /* the bitmap flow is from top to bottom, */ + /* but y is measured from bottom to top */ + if ( target->pitch < 0 ) + { + /* XXX */ + } + else + { + unsigned char* p = + source->buffer; + unsigned char* q = + target->buffer + + ( target->rows - y - source->rows ) * target->pitch + + x * 4; + unsigned char* limit_p = + p + source->pitch * (int)source->rows; + + + while ( p < limit_p ) + { + unsigned char* r = p; + unsigned char* s = q; + unsigned char* limit_r = r + source->width; + + + while ( r < limit_r ) + { + int aa = *r++; + int fa = color.alpha * aa / 255; + + int fb = color.blue * fa / 255; + int fg = color.green * fa / 255; + int fr = color.red * fa / 255; + + int ba2 = 255 - fa; + + int bb = s[0]; + int bg = s[1]; + int br = s[2]; + int ba = s[3]; + + + *s++ = (unsigned char)( bb * ba2 / 255 + fb ); + *s++ = (unsigned char)( bg * ba2 / 255 + fg ); + *s++ = (unsigned char)( br * ba2 / 255 + fr ); + *s++ = (unsigned char)( ba * ba2 / 255 + fa ); + } + + p += source->pitch; + q += target->pitch; + } + } + + atarget_offset->x = final_llx; + atarget_offset->y = final_lly + ( final_rows << 6 ); + + Error: + if ( error && free_target_bitmap_on_error ) + FT_Bitmap_Done( library, target ); + + if ( free_source_bitmap ) + FT_Bitmap_Done( library, &source_bitmap ); + + return error; + } + + /* documentation is in ftbitmap.h */ FT_EXPORT_DEF( FT_Error )