diff --git a/Changes b/Changes index d897ac06d..20b860572 100644 --- a/Changes +++ b/Changes @@ -1,5 +1,8 @@ Revision history for Perl module Prima +1.71 20XX-XX-XX + - Add DeviceBitmap.maskPixel + 1.70 2023-09-03 - Use fading effects in widgets - Solidify emulated 2D transformations with images diff --git a/class/DeviceBitmap.c b/class/DeviceBitmap.c index 507fc83d4..147e863e5 100644 --- a/class/DeviceBitmap.c +++ b/class/DeviceBitmap.c @@ -100,6 +100,29 @@ DeviceBitmap_get_paint_state( Handle self) return psEnabled; } +int +DeviceBitmap_maskPixel( Handle self, Bool set, int x, int y, int pixel) +{ + Point pt; + + if ( var->type != dbtLayered ) + return 0; + + pt = prima_matrix_apply_to_int( var->current_state.matrix, x, y ); + x = pt.x; + y = pt.y; + if (x >= var->w || x < 0 || y >= var->h || y < 0) + return clInvalid; + + if (set) { + if ( pixel < 0 ) pixel = 0; + if ( pixel > 255 ) pixel = 255; + return apc_gp_set_mask_pixel( self, x, y, pixel ); + } else { + return apc_gp_get_mask_pixel( self, x, y ); + } +} + #ifdef __cplusplus } #endif diff --git a/class/DeviceBitmap.cls b/class/DeviceBitmap.cls index 89aad97b3..8db3d8aff 100644 --- a/class/DeviceBitmap.cls +++ b/class/DeviceBitmap.cls @@ -2,6 +2,7 @@ object Prima::DeviceBitmap( Prima::Drawable) { int type; property int type; + property int maskPixel(int x, int y); method Bool begin_paint(); method Bool begin_paint_info(); method void done(); diff --git a/class/Icon.c b/class/Icon.c index aae48062c..49b110f9e 100644 --- a/class/Icon.c +++ b/class/Icon.c @@ -487,68 +487,64 @@ Icon_maskIndex( Handle self, Bool set, int index) return -1; } -SV * -Icon_maskPixel( Handle self, Bool set, int x, int y, SV * pixel) +int +Icon_maskPixel( Handle self, Bool set, int x, int y, int pixel) { Point pt; - if (!set) { - if ( opt_InPaint) - return inherited pixel(self,false,x,y,pixel); - - pt = prima_matrix_apply_to_int( VAR_MATRIX, x, y ); - x = pt.x; - y = pt.y; + pt = prima_matrix_apply_to_int( VAR_MATRIX, x, y ); + x = pt.x; + y = pt.y; + if (x >= var->w || x < 0 || y >= var->h || y < 0) + return clInvalid; - if (x >= var->w || x < 0 || y >= var->h || y < 0) - return newSViv(clInvalid); + if (!set) { + if ( is_opt( optInDraw) ) { + if ( var-> maskType == imbpp8 ) + return apc_gp_get_mask_pixel( self, x, y ); + } switch (var->maskType) { case imbpp1: { - Byte p = var->mask[ var->maskLine * y + ( x>>3 )]; + Byte p = var->mask[ var->maskLine * y + ( x >> 3 )]; p = (p >> (7 - (x & 7))) & 1; - return newSViv(p); + return p ? 0xff : 0; } case imbpp8: { - Byte p = var->mask[ var->lineSize * y + x]; - return newSViv(p); + Byte p = var->mask[ var->maskLine * y + x]; + return p; } default: - return newSViv(clInvalid); + return clInvalid; } } else { - IV color; - if ( is_opt( optInDraw)) - return inherited pixel(self,true,x,y,pixel); - - pt = prima_matrix_apply_to_int( VAR_MATRIX, x, y ); - x = pt.x; - y = pt.y; - - - if ( x >= var->w || x < 0 || y >= var->h || y < 0) - return NULL_SV; + Bool do_update = true; + if ( pixel < 0 ) pixel = 0; + if ( pixel > 255 ) pixel = 255; + if ( is_opt( optInDraw)) { + if ( var-> maskType == imbpp8 ) + return apc_gp_set_mask_pixel( self, x, y, pixel ); + else + do_update = false; + } - color = SvIV( pixel); - if ( color < 0 ) color = 0; - if ( color > 255 ) color = 255; switch (var->maskType) { case imbpp1 : { int x1 = 7 - ( x & 7 ); - Byte p = (color > 0) ? 1 : 0; + Byte p = (pixel > 0) ? 1 : 0; Byte *pd = var->mask + (var->maskLine * y + ( x >> 3)); *pd &= ~(1 << x1); *pd |= p << x1; break; } case imbpp8: - var->mask[var->maskLine * y + x] = color; + var->mask[var->maskLine * y + x] = pixel & 0xff; break; default: - return NULL_SV; + return 0; } - my->update_change( self); - return NULL_SV; + if ( do_update ) my->update_change( self); + return 0; } } diff --git a/class/Icon.cls b/class/Icon.cls index c8ea99061..b71ace9dd 100644 --- a/class/Icon.cls +++ b/class/Icon.cls @@ -17,7 +17,7 @@ object Prima::Icon( Prima::Image) property int maskType; property Color maskColor; property int maskIndex; - property SV * maskPixel( int x, int y); + property int maskPixel( int x, int y); property int autoMasking; method Handle bitmap(); method void init( HV * profile); diff --git a/class/Image/primitive.c b/class/Image/primitive.c index c5dcf8790..08ac0de24 100644 --- a/class/Image/primitive.c +++ b/class/Image/primitive.c @@ -19,7 +19,7 @@ Image_pixel( Handle self, Bool set, int x, int y, SV * pixel) #define BGRto32(pal) ((var->palette[pal].r<<16) | (var->palette[pal].g<<8) | (var->palette[pal].b)) if (!set) { - if ( opt_InPaint) + if ( is_opt( optInDraw)) return inherited pixel(self,false,x,y,pixel); pt = prima_matrix_apply_to_int( VAR_MATRIX, x, y ); diff --git a/include/apricot.h b/include/apricot.h index c5b670981..2fb9db3a7 100644 --- a/include/apricot.h +++ b/include/apricot.h @@ -3668,6 +3668,9 @@ END_TABLE(ggo,UV) extern int apc_gp_get_glyph_outline( Handle self, int index, int flags, int ** buffer); +extern Byte +apc_gp_get_mask_pixel( Handle self, int x, int y); + extern Color apc_gp_get_pixel( Handle self, int x, int y); @@ -3680,6 +3683,9 @@ apc_gp_put_image( Handle self, Handle image, int x, int y, extern Bool apc_gp_rectangle( Handle self, int x1, int y1, int x2, int y2); +extern Bool +apc_gp_set_mask_pixel( Handle self, int x, int y, Byte pixel); + extern Bool apc_gp_set_pixel( Handle self, int x, int y, Color color); diff --git a/pod/Prima/Image.pod b/pod/Prima/Image.pod index b3aa53e53..45a1cd162 100644 --- a/pod/Prima/Image.pod +++ b/pod/Prima/Image.pod @@ -706,6 +706,16 @@ A read-only property, returning the length of the mask row in bytes, as represented internally in memory. Data returned by the C<::mask> property is aligned with C<::maskLineSize> bytes per row. +=item maskPixel ( X_OFFSET, Y_OFFSET ) PIXEL + +Provides per-pixel access to the icon mask. + +In the disabled mode, gets and sets the value directly from the mask memory. +In the paint mode, and if (and only if) the mask depth is 8 bits, queries the +alpha pixel value from the system paint surface. Pixel values for all mask +depths are treated uniformly, their values range from 0 to 255. For example, +values for 1-bit mask pixels are 0 and 255, not 0 and 1. + =item maskType INTEGER Is either C (1) or C (8). The latter can be used @@ -720,6 +730,11 @@ that correspond to alpha values lesser than 255 will be reset to 0. =over +=item maskPixel ( X_OFFSET, Y_OFFSET ) PIXEL + +Provides per-pixel access to the alpha component of the layered device bitmap. +If the bitmap is not layered, the property does not do anything. + =item type INTEGER A read-only property that can only be set during creation, reflects whether the diff --git a/t/Object/GPExtra.t b/t/Object/GPExtra.t index 442904bed..ffd17107a 100644 --- a/t/Object/GPExtra.t +++ b/t/Object/GPExtra.t @@ -213,4 +213,37 @@ my $jj = $ii->data; $ii->begin_paint; $ii->end_paint; is(unpack('H*',$ii->data), unpack('H*',$jj), "begin/end paint preserves data"); + +if ( $can_argb ) { + my $j = Prima::Icon->new( + size => [1,1], + type => im::RGB, + maskType => 8, + autoMasking => am::None, + ); + $j->pixel(0,0,0x102030); + $j->maskPixel(0,0,0x40); + is( $j->pixel(0,0), 0x102030, "icon.pixel(1)"); + is( $j->maskPixel(0,0), 0x40, "icon.maskPixel(1)"); + $j->begin_paint; + is( $j->pixel(0,0), 0x102030, "icon.pixel(2)"); + is( $j->maskPixel(0,0), 0x40, "icon.maskPixel(2)"); + $j->pixel(0,0,0x203040); + $j->maskPixel(0,0,0x50); + is( $j->pixel(0,0), 0x203040, "icon.pixel(3)"); + is( $j->maskPixel(0,0), 0x50, "icon.maskPixel(3)"); + $j->end_paint; + is( $j->pixel(0,0), 0x203040, "icon.pixel(4)"); + is( $j->maskPixel(0,0), 0x50, "icon.maskPixel(4)"); + + $j = Prima::DeviceBitmap->new( + size => [1,1], + type => dbt::Layered, + ); + $j->pixel(0,0,0x102030); + $j->maskPixel(0,0,0x40); + is( $j->pixel(0,0), 0x102030, "dbm.pixel"); + is( $j->maskPixel(0,0), 0x40, "dbm.maskPixel"); +} + done_testing; diff --git a/unix/graphics.c b/unix/graphics.c index 620101c06..7a9d3eb7a 100644 --- a/unix/graphics.c +++ b/unix/graphics.c @@ -1142,6 +1142,59 @@ apc_gp_flood_fill( Handle self, int x, int y, Color color, Bool singleBorder) return ret; } +Byte +apc_gp_get_mask_pixel( Handle self, int x, int y) +{ + DEFXX; + XImage *im; + uint32_t p32 = 0; + int a, amax; + PRGBABitDescription bd; + + if ( !opt_InPaint) return clInvalid; + SHIFT( x, y); + XRENDER_SYNC; + + if ( x < 0 || x >= XX-> size.x || y < 0 || y >= XX-> size.y) + return 0; + if ( !((XT_IS_ICON(XX) || XT_IS_DBM(XX)) && XF_LAYERED(XX))) + return 0; + + im = XGetImage( DISP, XX-> gdrawable, x, XX-> size.y - y - 1, 1, 1, AllPlanes, ZPixmap); + XCHECKPOINT; + if ( !im) return clInvalid; + + bd = GET_RGBA_DESCRIPTION; + amax = 0xff; + + switch ( guts.argb_visual.depth ) { + case 16: + p32 = *(( uint16_t*)(im-> data)); + if ( guts.machine_byte_order != guts.byte_order) + p32 = REVERSE_BYTES_16(p32); + amax = 0xff & ( 0xff << ( 8 - bd-> alpha_range)); + goto COMP; + case 24: + p32 = (im-> data[0] << 16) | (im-> data[1] << 8) | im-> data[2]; + if ( guts.machine_byte_order != guts.byte_order) + p32 = REVERSE_BYTES_24(p32); + goto COMP; + case 32: + p32 = *(( uint32_t*)(im-> data)); + if ( guts.machine_byte_order != guts.byte_order) + p32 = REVERSE_BYTES_32(p32); + COMP: + a = ((((p32 & bd-> alpha_mask) >> bd->alpha_shift) << 8) >> bd-> alpha_range) & 0xff; + if ( a == amax ) a = 0xff; + break; + default: + warn("UAG_009: get_mask_pixel not implemented for %d depth", guts.argb_visual.depth); + } + + XDestroyImage( im); + return c; +} + Color apc_gp_get_pixel( Handle self, int x, int y) { @@ -1324,6 +1377,31 @@ apc_gp_set_palette( Handle self) return prima_palette_replace( self, false); } +Bool +apc_gp_set_mask_pixel( Handle self, int x, int y, int pixel) +{ + DEFXX; + + if ( PObject( self)-> options. optInDrawInfo) + return false; + if ( !XF_IN_PAINT(XX)) + return false; + if ( x < 0 || x >= XX-> size.x || y < 0 || y >= XX-> size.y) + return false; + if ( !((XT_IS_ICON(XX) || XT_IS_DBM(XX)) && XF_LAYERED(XX))) + return false; + XRENDER_SYNC; + + SHIFT(x, y); + XSetPlaneMask( DISP, XX-> gc, guts.argb_bits.alpha_mask); + XSetForeground( DISP, XX-> gc, DEV_A(&guts.argb_bits,pixel)); + XDrawPoint( DISP, XX-> gdrawable, XX-> gc, x, REVERT( y)); + XSetPlaneMask( DISP, XX-> gc, AllPlanes); + XFLUSH; + + return true; +} + Bool apc_gp_set_pixel( Handle self, int x, int y, Color color) { diff --git a/win32/image.c b/win32/image.c index b7c0a63cc..623d5f6b5 100644 --- a/win32/image.c +++ b/win32/image.c @@ -1993,6 +1993,43 @@ apc_dbm_get_handle( Handle self) return ( ApiHandle) sys ps; } +Byte +apc_gp_get_mask_pixel( Handle self, int x, int y) +{ + uint32_t *p; + PDrawable d = ( PDrawable ) self; + objCheck 0; + + if ( !((is_apt(aptIcon) || is_apt(aptDeviceBitmap)) && is_apt(aptLayered))) + return false; + select_world_transform(self, false); + SHIFT_XY(x,y); + if ( x < 0 || x >= d-> w || y < 0 || y > d-> h) return 0; + + p = sys s. image. argb_bits + d-> h * y + x; + return (*p) >> 24; +} + +Bool +apc_gp_set_mask_pixel( Handle self, int x, int y, Byte pixel) +{ + uint32_t *p; + PDrawable d = ( PDrawable ) self; + objCheck false; + + if ( !((is_apt(aptIcon) || is_apt(aptDeviceBitmap)) && is_apt(aptLayered))) + return false; + select_world_transform(self, false); + SHIFT_XY(x,y); + if ( x < 0 || x >= d-> w || y < 0 || y > d-> h) return false; + + p = sys s. image. argb_bits + d-> h * y + x; + *p &= 0x00ffffff; + *p |= ((uint32_t) pixel) << 24; + + return true; +} + #ifdef __cplusplus }