Skip to content

Instantly share code, notes, and snippets.

@nanjizal
Last active October 14, 2023 23:55
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save nanjizal/f2db86737fb089744cb3aa100197b18b to your computer and use it in GitHub Desktop.
Save nanjizal/f2db86737fb089744cb3aa100197b18b to your computer and use it in GitHub Desktop.
testing Bytes Colour
// https://try.haxe.org/#f7d671F9
import haxe.io.Bytes;
import haxe.io.UInt32Array;
function main() {
trace("Haxe is great!");
var width = 10;
var height = 10;
var a1 = new ByteImage( width, height );
a1.set( 1, 0x1234567A );
trace( a1.get( 1 ) );
var a2 = new UInt32Wrap( Std.int( width * height ) );
a2.set( 1, 0x1234567A );
trace( a2.get( 1 ) );
var v1: ByteImage = cast testWrap( cast a1 );
trace( v1.get(1).stringHash() );
trace( a1.image.toHex() );
var v2: UInt32Wrap = cast testWrap( cast a2 );
trace( v2.get(1).stringHash() );
}
function testWrap<T:ImageWrapper>(a:T){
return (a:T);
}
typedef ImageWrapper = {
public function set( key: Int, val: Pixel32 ): Pixel32;
public function get( key: Int ): Pixel32;
}
abstract UInt32Wrap( UInt32Array ){
public inline
function new( v: Int ){
this = new UInt32Array( v );
}
public inline
function set( key: Int, v: Pixel32 ): Pixel32 {
return ( this.set( key, new Pixel32( v ) ): Pixel32 );
}
public inline
function get( key: Int ): Pixel32 {
return ( this.get( key ) : Pixel32 );
}
}
@:allow(Test)
@:structInit
class ByteImage_ {
public var width: Int;
public var height: Int;
public var image: Bytes;
public function new( width: Int, height: Int, image: Bytes ){
this.width = width;
this.height = height;
this.image = image;
}
}
@:forward
@:transient
abstract ByteImage( ByteImage_ ) from ByteImage_ to ByteImage_ {
public inline
function new( w: Int, h: Int ){
this = { width: w, height: h, image: Bytes.alloc( w * h * 4 + h ) };
zero();
}
public inline
function zero(){
var w = 0, row = 0;
for( y in 0...this.height ) {
for( x in 0...this.width ) {
this.image.set(w++,0);
this.image.set(w++,0);
this.image.set(w++,0);
this.image.set(w++,0);
row += 4;
}
}
}
inline
function rowWidth(): Int {
return this.width * 4;
}
inline
function get_y( key: Int ): Int {
return Math.floor( key/this.height );
}
inline
function wholeRows( y: Int ){
return y * rowWidth();
}
inline
function colPos( y: Int, key: Int ){
return key - ( y * this.height );
}
inline
function get_xOff( y: Int, key: Int ){
return 4*colPos( y, key );
}
public inline
function pos( key: Int ){
final y = get_y( key );
return wholeRows( y ) + get_xOff( y, key );
}
public inline
function location( px: Float, py: Float ): Int {
// key
return Std.int( py * this.width + px );
}
@:arrayAccess
public inline
function get( key: Int ): Pixel32 {
final p = pos( key );
return new Pixel32( this.image.getInt32(p) );
}
@:arrayAccess
public inline
function set( key: Int, col: Pixel32 ): Pixel32 {
final p = pos( key );
this.image.setInt32( p, col );
return col;
}
}
@:forward
@:transient
abstract PixelChannel( Int ) to Int from Int {
inline
public function new( v: Int )
this = v;
@:from
public inline static function toHexInt( c: Float ): PixelChannel
return ( cast Math.round( c * 255 ) : PixelChannel );
@:to
public inline function colIntToFloat(): Float
return ( this == 0 )? 0.: this/255;
@:to
public inline function stringHash(): String
return '#' + StringTools.hex( this, 2 );
inline
public static function boundChannel( f: Float ): Int {
var i = Std.int( f );
if( i > 0xFF ) i = 0xFF;
if( i < 0 ) i = 0;
return new PixelChannel( i );
}
}
@:forward
@:transient
abstract Pixel32( Int ) to Int from Int {
inline
public function new( v: Int )
this = v;
/**
returns the 0x00 -> 0xFF number component
in ARGB, 0 -> B, 1 -> G, 2 - R, 3 -> A
**/
inline
public function hexChannel( i: Int ): PixelChannel {
return switch( i ){
case 0:
( cast this >> 24 & 0xFF: PixelChannel );
case 1:
( cast this >> 16 & 0xFF: PixelChannel );
case 2:
( cast this >> 8 & 0xFF : PixelChannel );
case 3:
( cast this & 0xFF: PixelChannel );
case _:
( 0x00: PixelChannel );
}
}
public var c0( get, set ): PixelChannel;
inline
function get_c0(): PixelChannel
return ( cast this >> 24 & 0xFF: PixelChannel );
inline
function set_c0( v: PixelChannel ): PixelChannel {
this = Pixel32.fromChannels( v, c1, c2, c3 );
return v;
}
public var c1( get, set ): PixelChannel;
inline
function get_c1(): PixelChannel
return ( cast this >> 16 & 0xFF: PixelChannel );
inline
function set_c1( v: PixelChannel ): PixelChannel {
this = Pixel32.fromChannels( c0, v, c2, c3 );
return v;
}
public var c2( get, set ): PixelChannel;
inline
function get_c2(): PixelChannel
return ( cast this >> 8 & 0xFF : PixelChannel );
inline
function set_c2( v: PixelChannel ): PixelChannel {
this = Pixel32.fromChannels( c0, c1, v, c3 );
return v;
}
public var c3( get, set ): PixelChannel;
inline
function get_c3(): PixelChannel
return ( cast this & 0xFF: PixelChannel );
inline
function set_c3( v: PixelChannel ): PixelChannel {
this = Pixel32.fromChannels( c0, c1, c2, v );
return v;
}
inline
public function flip13(): Pixel32
return ( cast c0 << 24 | c3 << 16 | c2 << 8 | c1 : Pixel32 );
/*
inline
public function transferColor(): Pixel32
return ( isLittleEndian )? flip13(): ( cast this: Pixel32 );
*/
inline
public function stringHash(): String
return '#' + StringTools.hex( this, 8 );
inline
public function isTransparent(): Bool
return ( cast c0: Int ) < 0xFE;
/*
inline
public static function fromPixel28Alpha( col: Pixel28, alpha: Int ): Pixel32
return ( cast ( alpha << 24 | col ) : Pixel32 );
*/
inline
public static function fromChannels( ch0: Int, ch1: Int, ch2: Int, ch3: Int ): Pixel32
return ( cast ch0 << 24 | ch1 << 16 | ch2 << 8 | ch3 : Pixel32 );
inline
public static function from_argb( a: Float, r: Float, g: Float, b: Float ): Pixel32
return ( cast
( PixelChannel.toHexInt( a ) << 24 )
| ( PixelChannel.toHexInt( r ) << 16 )
| ( PixelChannel.toHexInt( g ) << 8 )
| PixelChannel.toHexInt( b ): Pixel32 );
inline
public function maskPixel( m: Pixel32 ): Pixel32 {
return if( m*1 == 0 ){
return new Pixel32( this );
} else {
var m0: Float = m.c0;
var m1: Float = m.c1;
var m2: Float = m.c2;
var m3: Float = m.c3;
// may need some extra logic for round error especially at 1, 0?
var ch0 = Std.int( (1.-m0)*abstract.c0 );
var ch1 = Std.int( (1.-m1)*abstract.c1 );
var ch2 = Std.int( (1.-m2)*abstract.c2 );
var ch3 = Std.int( (1.-m3)*abstract.c3 );
Pixel32.from_argb( ch0, ch1, ch2, ch3 );
}
}
public inline
function channelBlend( ch0: PixelChannel
, ch1: PixelChannel
, ch2: PixelChannel
, ch3: PixelChannel ){
var a1: Float = c0; // abstract conversion
var r1: Float = c1;
var g1: Float = c2;
var b1: Float = c3;
var a2: Float = ch0;
var r2: Float = ch1;
var g2: Float = ch2;
var b2: Float = ch3;
var a3 = a1 * ( 1 - a2 );
var r = colBlendFunc( r1, r2, a3, a2 );
var g = colBlendFunc( g1, g2, a3, a2 );
var b = colBlendFunc( b1, b2, a3, a2 );
var a = alphaBlendFunc( a3, a2 );
return fromChannels( a, r, g, b );
}
// does not flip colors
@:op(A + B) public inline
function alphaBlend( rhs: Pixel32 ): Pixel32 {
var a1: Float = c0; // abstract conversion
var r1: Float = c1;
var g1: Float = c2;
var b1: Float = c3;
var a2: Float = rhs.c0;
var r2: Float = rhs.c1;
var g2: Float = rhs.c2;
var b2: Float = rhs.c3;
var a3 = a1 * ( 1 - a2 );
var r = colBlendFunc( r1, r2, a3, a2 );
var g = colBlendFunc( g1, g2, a3, a2 );
var b = colBlendFunc( b1, b2, a3, a2 );
var a = alphaBlendFunc( a3, a2 );
return fromChannels( a, r, g, b );
}
inline
static function colBlendFunc( x1: Float, x2: Float, a3: Float, a2: Float ): Int
return Std.int( 255 * ( x1 * a3 + x2 * a2 ) );
inline
static function alphaBlendFunc( a3: Float, a2: Float ): Int
return Std.int( 255 * ( a3 + a2 ) );
}
import haxe.io.Bytes;
import haxe.io.UInt32Array;
import haxe.ds.Either;
function main() {
trace("Haxe is great!");
var width = 10;
var height = 10;
var a1 = new ByteImage( width, height );
a1.set( 3, 0x1234567A );
trace( a1.get( 3 ) );
var a2 = new UInt32Wrap( Std.int( width * height ) );
a2.set( 2, 0x1234567A );
trace( a2.get( 2 ) );
var v1: ByteImage = cast testWrap( cast a1 );
trace( v1.get(2).stringHash() );
trace( a1.image.toHex() );
var a3: OneOf<ByteImage,UInt32Wrap> = a2;
//var v2: Either<ByteImage,UInt32Wrap> = a3;
//trace( v2 );
//trace( v2.get(1).stringHash() );
switch(a3) {
case Left(l): trace( l.get(2).stringHash() );
case Right(r): trace( r.get(2).stringHash() );
case _: trace('oops');
}
var a4: OneOf<ByteImage,UInt32Wrap> = a1;
switch(a4){
case Left(l): trace( l.get(2).stringHash() );
case Right(r): trace( r.get(2).stringHash() );
case _: trace('oops');
}
}
abstract OneOf<A, B>(Either<A, B>) from Either<A, B> to Either<A, B> {
@:from inline static function fromA<A, B>(a:A):OneOf<A, B> {
return Left(a);
}
@:from inline static function fromB<A, B>(b:B):OneOf<A, B> {
return Right(b);
}
@:to inline function toA():Null<A> return switch(this) {
case Left(a): a;
default: null;
}
@:to inline function toB():Null<B> return switch(this) {
case Right(b): b;
default: null;
}
}
function testWrap<T:ImageWrapper>(a:T){
return (a:T);
}
typedef ImageWrapper = {
public function set( key: Int, val: Pixel32 ): Pixel32;
public function get( key: Int ): Pixel32;
}
@:keep
abstract UInt32Wrap( UInt32Array ){
public inline
function new( v: Int ){
this = new UInt32Array( v );
}
public inline
function set( key: Int, v: Pixel32 ): Pixel32 {
return ( this.set( key, new Pixel32( v ) ): Pixel32 );
}
public inline
function get( key: Int ): Pixel32 {
return ( this.get( key ) : Pixel32 );
}
}
@:allow(Test)
@:structInit
class ByteImage_ {
public var width: Int;
public var height: Int;
public var image: Bytes;
public function new( width: Int, height: Int, image: Bytes ){
this.width = width;
this.height = height;
this.image = image;
}
}
@:forward
@:transient
abstract ByteImage( ByteImage_ ) from ByteImage_ to ByteImage_ {
public inline
function new( w: Int, h: Int ){
this = { width: w, height: h, image: Bytes.alloc( w * h * 4 + h ) };
zero();
}
public inline
function zero(){
var w = 0, row = 0;
for( y in 0...this.height ) {
for( x in 0...this.width ) {
this.image.set(w++,0);
this.image.set(w++,0);
this.image.set(w++,0);
this.image.set(w++,0);
row += 4;
}
}
}
inline
function rowWidth(): Int {
return this.width * 4;
}
inline
function get_y( key: Int ): Int {
return Math.floor( key/this.height );
}
inline
function wholeRows( y: Int ){
return y * rowWidth();
}
inline
function colPos( y: Int, key: Int ){
return key - ( y * this.height );
}
inline
function get_xOff( y: Int, key: Int ){
return 4*colPos( y, key );
}
public inline
function pos( key: Int ){
final y = get_y( key );
//return 4*( y * this.width - y * this.height + 1 );
return wholeRows( y ) + get_xOff( y, key );
}
public inline
function location( px: Float, py: Float ): Int {
// key
return Std.int( py * this.width + px );
}
@:arrayAccess
public inline
function get( key: Int ): Pixel32 {
final p = pos( key );
return new Pixel32( this.image.getInt32(p) );
}
@:arrayAccess
public inline
function set( key: Int, col: Pixel32 ): Pixel32 {
final p = pos( key );
this.image.setInt32( p, col );
return col;
}
}
@:forward
@:transient
abstract PixelChannel( Int ) to Int from Int {
inline
public function new( v: Int )
this = v;
@:from
public inline static function toHexInt( c: Float ): PixelChannel
return ( cast Math.round( c * 255 ) : PixelChannel );
@:to
public inline function colIntToFloat(): Float
return ( this == 0 )? 0.: this/255;
@:to
public inline function stringHash(): String
return '#' + StringTools.hex( this, 2 );
inline
public static function boundChannel( f: Float ): Int {
var i = Std.int( f );
if( i > 0xFF ) i = 0xFF;
if( i < 0 ) i = 0;
return new PixelChannel( i );
}
}
@:forward
@:transient
abstract Pixel32( Int ) to Int from Int {
inline
public function new( v: Int )
this = v;
/**
returns the 0x00 -> 0xFF number component
in ARGB, 0 -> B, 1 -> G, 2 - R, 3 -> A
**/
inline
public function hexChannel( i: Int ): PixelChannel {
return switch( i ){
case 0:
( cast this >> 24 & 0xFF: PixelChannel );
case 1:
( cast this >> 16 & 0xFF: PixelChannel );
case 2:
( cast this >> 8 & 0xFF : PixelChannel );
case 3:
( cast this & 0xFF: PixelChannel );
case _:
( 0x00: PixelChannel );
}
}
public var c0( get, set ): PixelChannel;
inline
function get_c0(): PixelChannel
return ( cast this >> 24 & 0xFF: PixelChannel );
inline
function set_c0( v: PixelChannel ): PixelChannel {
this = Pixel32.fromChannels( v, c1, c2, c3 );
return v;
}
public var c1( get, set ): PixelChannel;
inline
function get_c1(): PixelChannel
return ( cast this >> 16 & 0xFF: PixelChannel );
inline
function set_c1( v: PixelChannel ): PixelChannel {
this = Pixel32.fromChannels( c0, v, c2, c3 );
return v;
}
public var c2( get, set ): PixelChannel;
inline
function get_c2(): PixelChannel
return ( cast this >> 8 & 0xFF : PixelChannel );
inline
function set_c2( v: PixelChannel ): PixelChannel {
this = Pixel32.fromChannels( c0, c1, v, c3 );
return v;
}
public var c3( get, set ): PixelChannel;
inline
function get_c3(): PixelChannel
return ( cast this & 0xFF: PixelChannel );
inline
function set_c3( v: PixelChannel ): PixelChannel {
this = Pixel32.fromChannels( c0, c1, c2, v );
return v;
}
inline
public function flip13(): Pixel32
return ( cast c0 << 24 | c3 << 16 | c2 << 8 | c1 : Pixel32 );
/*
inline
public function transferColor(): Pixel32
return ( isLittleEndian )? flip13(): ( cast this: Pixel32 );
*/
inline
public function stringHash(): String
return '#' + StringTools.hex( this, 8 );
inline
public function isTransparent(): Bool
return ( cast c0: Int ) < 0xFE;
/*
inline
public static function fromPixel28Alpha( col: Pixel28, alpha: Int ): Pixel32
return ( cast ( alpha << 24 | col ) : Pixel32 );
*/
inline
public static function fromChannels( ch0: Int, ch1: Int, ch2: Int, ch3: Int ): Pixel32
return ( cast ch0 << 24 | ch1 << 16 | ch2 << 8 | ch3 : Pixel32 );
inline
public static function from_argb( a: Float, r: Float, g: Float, b: Float ): Pixel32
return ( cast
( PixelChannel.toHexInt( a ) << 24 )
| ( PixelChannel.toHexInt( r ) << 16 )
| ( PixelChannel.toHexInt( g ) << 8 )
| PixelChannel.toHexInt( b ): Pixel32 );
inline
public function maskPixel( m: Pixel32 ): Pixel32 {
return if( m*1 == 0 ){
return new Pixel32( this );
} else {
var m0: Float = m.c0;
var m1: Float = m.c1;
var m2: Float = m.c2;
var m3: Float = m.c3;
// may need some extra logic for round error especially at 1, 0?
var ch0 = Std.int( (1.-m0)*abstract.c0 );
var ch1 = Std.int( (1.-m1)*abstract.c1 );
var ch2 = Std.int( (1.-m2)*abstract.c2 );
var ch3 = Std.int( (1.-m3)*abstract.c3 );
Pixel32.from_argb( ch0, ch1, ch2, ch3 );
}
}
public inline
function channelBlend( ch0: PixelChannel
, ch1: PixelChannel
, ch2: PixelChannel
, ch3: PixelChannel ){
var a1: Float = c0; // abstract conversion
var r1: Float = c1;
var g1: Float = c2;
var b1: Float = c3;
var a2: Float = ch0;
var r2: Float = ch1;
var g2: Float = ch2;
var b2: Float = ch3;
var a3 = a1 * ( 1 - a2 );
var r = colBlendFunc( r1, r2, a3, a2 );
var g = colBlendFunc( g1, g2, a3, a2 );
var b = colBlendFunc( b1, b2, a3, a2 );
var a = alphaBlendFunc( a3, a2 );
return fromChannels( a, r, g, b );
}
// does not flip colors
@:op(A + B) public inline
function alphaBlend( rhs: Pixel32 ): Pixel32 {
var a1: Float = c0; // abstract conversion
var r1: Float = c1;
var g1: Float = c2;
var b1: Float = c3;
var a2: Float = rhs.c0;
var r2: Float = rhs.c1;
var g2: Float = rhs.c2;
var b2: Float = rhs.c3;
var a3 = a1 * ( 1 - a2 );
var r = colBlendFunc( r1, r2, a3, a2 );
var g = colBlendFunc( g1, g2, a3, a2 );
var b = colBlendFunc( b1, b2, a3, a2 );
var a = alphaBlendFunc( a3, a2 );
return fromChannels( a, r, g, b );
}
inline
static function colBlendFunc( x1: Float, x2: Float, a3: Float, a2: Float ): Int
return Std.int( 255 * ( x1 * a3 + x2 * a2 ) );
inline
static function alphaBlendFunc( a3: Float, a2: Float ): Int
return Std.int( 255 * ( a3 + a2 ) );
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment