Skip to content

[BRL.Pixmap] SuperStrict mode. #361

@mingwya

Description

@mingwya

SuperStrict

Rem
bbdoc: Graphics/Pixmaps
End Rem
Module BRL.Pixmap

ModuleInfo "Version: 1.08"
ModuleInfo "Author: Mark Sibly"
ModuleInfo "License: zlib/libpng"
ModuleInfo "Copyright: Blitz Research Ltd"
ModuleInfo "Modserver: BRL"

ModuleInfo "History: 1.08 Release"
ModuleInfo "History: Changed to SuperStrict mode"
ModuleInfo "History: 1.07 Release"
ModuleInfo "History: Added ClearPixels"
ModuleInfo "History: 1.06 Release"
ModuleInfo "History: Added new GL compatible pixel formats"
ModuleInfo "History: 1.05 Release"
ModuleInfo "History: Added _source:Object field"
ModuleInfo "History: Removed AddPixmapLoader function"

Import BRL.Math
Import BRL.Stream
Import BRL.Color

Import "pixel.bmx"

Rem
bbdoc: The Pixmap type
end rem
Type TPixmap

	Method _pad()
	End Method

	Rem
	bbdoc: A byte pointer to the pixmap's pixels
	end rem
	Field pixels:Byte Ptr
	
	Rem
	bbdoc: The width, in pixels, of the pixmap
	end rem
	Field width:Int
	
	Rem
	bbdoc: The height, in pixels, of the pixmap
	end rem
	Field height:Int
	
	Rem
	bbdoc: The pitch, in bytes, of the pixmap
	end rem
	Field pitch:Int
	
	Rem
	bbdoc: The pixel format of the pixmap
	end rem
	Field format:Int
	
	Rem
	bbdoc: The capacity, in bytes, of the pixmap, or -1 for a static pixmap
	end rem
	Field capacity:Int
	
	'Hack to provide robust PixmapWindow functionality
	Field _source:Object
	
	Field dds_fmt:Int=0 ' dds format
	
	Field tex_name:Int=0 ' texture name
	
	Method Delete()
		If capacity>=0 
			MemFree pixels
		EndIf
	End Method

	Rem
	bbdoc: Get memory address of a pixel
	returns: A byte pointer to the pixel at coordinates @x, @y
	End Rem
	Method PixelPtr:Byte Ptr( x:Int,y:Int )
		Return pixels+y*pitch+x*BytesPerPixel[format]
	End Method

	Rem
	bbdoc: Create a virtual window into a pixmap
	returns: A static pixmap that references the specified rectangle.
	End Rem
	Method Window:TPixmap( x:Int,y:Int,width:Int,height:Int )
		Assert..
		x>=0 And width>=0 And x+width<=Self.width And..
		y>=0 And height>=0 And y+height<=Self.height Else "Pixmap coordinates out of bounds"
		Local t:TPixmap=CreateStatic( PixelPtr(x,y),width,height,pitch,format )
		t._source=Self
		Return t
	End Method

	Rem
	bbdoc: Duplicate a pixmap
	returns: A new TPixmap object.
	end rem
	Method Copy:TPixmap()
		Local pixmap:TPixmap=Create( width:Int,height:Int,format:Int )
		For Local y:Int=0 Until height
			CopyPixels Self.PixelPtr(0,y),pixmap.PixelPtr(0,y),format,width
		Next
		Return pixmap
	End Method

	Rem
	bbdoc: Paste a pixmap
	end rem
	Method Paste( source:TPixmap,x:Int,y:Int )
		For Local h:Int=0 Until source.height
			ConvertPixels source.PixelPtr(0,h),source.format,Self.PixelPtr(x,y+h),Self.format,source.width
		Next
	End Method
	
	Rem
	bbdoc: Convert a pixmap
	returns: A new TPixmap object in the specified format
	end rem
	Method Convert:TPixmap( format:Int )
		Local pixmap:TPixmap=Create( width,height,format )
		For Local y:Int=0 Until height
			ConvertPixels Self.PixelPtr(0,y),Self.format,pixmap.PixelPtr(0,y),pixmap.format,pixmap.width
		Next
		Return pixmap
	End Method
	
	Rem
	bbdoc: Read a pixel from a pixmap
	returns: The pixel at the specified coordinates packed into an integer
	End Rem
	Method ReadPixel:Int( x:Int,y:Int )
		Assert x>=0 And x<width And y>=0 And y<height Else "Pixmap coordinates out of bounds"
		Local p:Byte Ptr=PixelPtr(x,y)
		Select format
		Case PF_A8
			Return p[0] Shl 24 | $00ffffff
		Case PF_I8
			Return $ff000000 | p[0] Shl 16 | p[0] Shl 8 | p[0]
		Case PF_RGB888
			Return $ff000000 | p[0] Shl 16 | p[1] Shl 8 | p[2]
		Case PF_BGR888
			Return $ff000000 | p[2] Shl 16 | p[1] Shl 8 | p[0]
		Case PF_RGBA8888
			Return p[0] Shl 16 | p[1] Shl 8 | p[2] | p[3] Shl 24
		Case PF_BGRA8888
			Return p[2] Shl 16 | p[1] Shl 8 | p[0] | p[3] Shl 24
		End Select
	End Method

	Method ReadPixelColor:SColor8( x:Int,y:Int )
		Return New SColor8(ReadPixel(x, y))
	End Method

	Rem
	bbdoc: Write a pixel to a pixmap
	end rem
	Method WritePixel( x:Int,y:Int,argb:Int )
		Assert x>=0 And x<width And y>=0 And y<height Else "Pixmap coordinates out of bounds"
		Local p:Byte Ptr=PixelPtr(x,y)
		Select format
		Case PF_A8
			p[0]=argb Shr 24
		Case PF_I8
			p[0]=( (argb Shr 16 & $ff)+(argb Shr 8 & $ff)+(argb & $ff) )/3
		Case PF_RGB888
			p[0]=argb Shr 16 ; p[1]=argb Shr 8 ; p[2]=argb
		Case PF_BGR888
			p[0]=argb ; p[1]=argb Shr 8 ; p[2]=argb Shr 16
		Case PF_RGBA8888
			p[0]=argb Shr 16 ; p[1]=argb Shr 8 ; p[2]=argb ; p[3]=argb Shr 24
		Case PF_BGRA8888
			p[0]=argb ; p[1]=argb Shr 8 ; p[2]=argb Shr 16 ; p[3]=argb Shr 24
		End Select
	End Method
	
	Rem
	bbdoc: Writes a pixel to a pixmap of the specified #SColor8
	End Rem
	Method WritePixel(x:Int, y:Int, col:SColor8)
		Assert x>=0 And x<width And y>=0 And y<height Else "Pixmap coordinates out of bounds"
		Local p:Byte Ptr=PixelPtr(x,y)
		Select format
		Case PF_A8
			p[0]=col.a
		Case PF_I8
			p[0]=(col.r + col.g + col.b)/3
		Case PF_RGB888
			p[0]=col.r ; p[1]=col.g ; p[2]=col.b
		Case PF_BGR888
			p[0]=col.b ; p[1]=col.g ; p[2]=col.r
		Case PF_RGBA8888
			p[0]=col.r ; p[1]=col.g ; p[2]=col.b; p[3]=col.a
		Case PF_BGRA8888
			p[0]=col.b ; p[1]=col.g ; p[2]=col.r ; p[3]=col.a
		End Select	
	End Method
	
	Rem
	bbdoc: Create a pixmap
	returns: A new TPixmap object
	end rem	
	Function Create:TPixmap( width:Int,height:Int,format:Int,align:Int=4 )
		Local pitch:Int=width*BytesPerPixel[format]
		pitch=(pitch+(align-1))/align*align
		Local capacity:Size_T=pitch*height
		Local pixmap:TPixmap=New TPixmap
		pixmap.pixels=MemAlloc( capacity )
		pixmap.width=width
		pixmap.height=height
		pixmap.pitch=pitch
		pixmap.format=format
		pixmap.capacity=capacity
		Return pixmap
	End Function

	Rem
	bbdoc: Create a static pixmap
	returns: A new TPixmap object
	end rem
	Function CreateStatic:TPixmap( pixels:Byte Ptr,width:Int,height:Int,pitch:Int,format:Int )
		Local pixmap:TPixmap=New TPixmap
		pixmap.pixels=pixels
		pixmap.width=width
		pixmap.height=height
		pixmap.pitch=pitch
		pixmap.format=format
		pixmap.capacity=-1
		Return pixmap
	End Function

	Rem
	bbdoc: Clear a pixmap
	End Rem	
	Method ClearPixels( argb:Int )
		If Not argb And width*BytesPerPixel[format]=pitch
			MemClear pixels,Size_T(pitch*height)
			Return
		EndIf
		For Local y:Int=0 Until height
			Local p:Byte Ptr=PixelPtr(0,y)
			If Not argb
				MemClear p,Size_T(width*BytesPerPixel[format])
				Continue
			EndIf			
			Select format
			Case PF_A8
				For Local x:Int=0 Until width
					p[x]=argb Shr 24
				Next
			Case PF_I8
				For Local x:Int=0 Until width
					p[x]=( (argb Shr 16 & $ff)+(argb Shr 8 & $ff)+(argb & $ff) )/3
				Next
			Case PF_RGB888
				For Local x:Int=0 Until width*3 Step 3
					p[x]=argb Shr 16 ; p[x+1]=argb Shr 8 ; p[x+2]=argb
				Next
			Case PF_BGR888
				For Local x:Int=0 Until width*3 Step 3
					p[x]=argb ; p[x+1]=argb Shr 8 ; p[x+2]=argb Shr 16
				Next
			Case PF_RGBA8888
				For Local x:Int=0 Until width*4 Step 4
					p[x]=argb Shr 16 ; p[x+1]=argb Shr 8 ; p[x+2]=argb ; p[x+3]=argb Shr 24
				Next
			Case PF_BGRA8888
				For Local x:Int=0 Until width*4 Step 4
					p[x]=argb ; p[x+1]=argb Shr 8 ; p[x+2]=argb Shr 16 ; p[x+3]=argb Shr 24
				Next
			End Select
		Next
	End Method
	
	Method ClearPixels( color:SColor8 )
		ClearPixels(color.ToARGB())
	End Method

	
End Type

Private

Global pixmap_loaders:TPixmapLoader

Public

Rem
bbdoc: Abstract base type for pixmap loaders
about:
To create a new pixmap loader, you should extend TPixmapLoader and implement the #LoadPixmap method.

To install your pixmap loader, simply create an instance of it using #New.
End Rem
Type TPixmapLoader
	Field _succ:TPixmapLoader
	
	Method New()
		_succ=pixmap_loaders
		pixmap_loaders=Self
	End Method
	
	Rem
	bbdoc: Load a pixmap
	about: This method must be implemented by extending types.
	end rem
	Method LoadPixmap:TPixmap( stream:TStream ) Abstract
	
End Type

Rem
bbdoc: Create a pixmap
returns: A new pixmap object of the specified @width and @height
about:
@format should be one of the following:

[ @Format | @Description
* PF_A8 | 8 bit alpha
* PF_I8 | 8 bit intensity
* PF_RGB888 | 24 bit big endian RGB
* PF_BGR888 | 24 bit little endian RGB
* PF_RGBA8888 | 32 bit big endian RGB with alpha
* PF_BGRA8888 | 32 bit little endian RGB with alpha
]

Note that the newly created pixmap will contain random data. #ClearPixels can
be used to set all pixels to a known value prior to use.
End Rem
Function CreatePixmap:TPixmap( width:Int,height:Int,format:Int,align_bytes:Int=4 )
	Return TPixmap.Create( width,height,format,align_bytes )
End Function

Rem
bbdoc: Create a pixmap with existing pixel data
returns: A new pixmap object that references an existing block of memory
about:
The memory referenced by a static pixmap is not released when the pixmap is deleted.

See #CreatePixmap for valid pixmap formats.
End Rem
Function CreateStaticPixmap:TPixmap( pixels:Byte Ptr,width:Int,height:Int,pitch:Int,format:Int )
	Return TPixmap.CreateStatic( pixels,width,height,pitch,format )
End Function

Rem
bbdoc: Copy a pixmap
returns: A new pixmap object
end rem
Function CopyPixmap:TPixmap( pixmap:TPixmap )
	Return pixmap.Copy()
End Function

Rem
bbdoc: Convert pixel format of a pixmap
returns: A new pixmap object with the specified pixel format
about:
See #CreatePixmap for valid pixmap formats.
end rem
Function ConvertPixmap:TPixmap( pixmap:TPixmap,format:Int )
	Return pixmap.Convert( format )
End Function

Rem
bbdoc: Get pixmap width
returns: The width, in pixels, of @pixmap
end rem
Function PixmapWidth:Int( pixmap:TPixmap )
	Return pixmap.width
End Function

Rem
bbdoc: Get pixmap width
returns: The height, in pixels, of @pixmap
end rem
Function PixmapHeight:Int( pixmap:TPixmap )
	Return pixmap.height
End Function

Rem
bbdoc: Get pixmap pitch
returns: The pitch, in bytes, of @pixmap
about:
Pitch refers to the difference, in bytes, between the start of one row of pixels and the start of the next row.
end rem
Function PixmapPitch:Int( pixmap:TPixmap )
	Return pixmap.pitch
End Function

Rem
bbdoc: Get pixmap format
returns: The format of the pixels stored in @pixmap
about:
See #CreatePixmap for supported formats.
End Rem
Function PixmapFormat:Int( pixmap:TPixmap )
	Return pixmap.format
End Function

Rem
bbdoc: Get pixmap pixels
returns: A byte pointer to the pixels stored in @pixmap
end rem
Function PixmapPixelPtr:Byte Ptr( pixmap:TPixmap,x:Int=0,y:Int=0 )
	Return pixmap.PixelPtr( x,y )
End Function

Rem
bbdoc: Create a pixmap window
returns: A new pixmap object
about: #PixmapWindow creates a 'virtual' window into @pixmap.
end rem
Function PixmapWindow:TPixmap( pixmap:TPixmap,x:Int,y:Int,width:Int,height:Int )
	Return pixmap.Window( x,y,width,height )
End Function

Rem
bbdoc: Mask a pixmap
returns: A new pixmap object
about: @MaskPixmap builds a new pixmap with alpha components set to '0' wherever the pixel colors
in the original @pixmap match @mask_red, @mask_green and @mask_blue. @mask_red, @mask_green and @mask_blue
should be in the range 0 to 255.
end rem
Function MaskPixmap:TPixmap( pixmap:TPixmap,mask_red:Int,mask_green:Int,mask_blue:Int ) NoDebug

	Local tmp:TPixmap=pixmap
	If tmp.format<>PF_RGBA8888 tmp=tmp.Convert( PF_RGBA8888 )
	
	Local out:TPixmap=CreatePixmap( tmp.width,tmp.height,PF_RGBA8888 )
	
	For Local y:Int=0 Until pixmap.height
		Local t:Byte Ptr=tmp.PixelPtr( 0,y )
		Local o:Byte Ptr=out.PixelPtr( 0,y )
		For Local x:Int=0 Until pixmap.width
			If t[0]<>mask_red Or t[1]<>mask_green Or t[2]<>mask_blue
				o[0]=t[0]
				o[1]=t[1]
				o[2]=t[2]
				o[3]=255
			Else
				Local r:Int,g:Int,b:Int,n:Int
				For Local ty:Int=y-1 To y+1
					Local t:Byte Ptr=tmp.pixelptr( x-1,ty )
					For Local tx:Int=x-1 To x+1
						If tx>=0 And tx<tmp.width And ty>=0 And ty<tmp.height
							If t[0]<>mask_red Or t[1]<>mask_green Or t[2]<>mask_blue
								r:+t[0]
								g:+t[1]
								b:+t[2]
								n:+1
							EndIf
						EndIf
						t:+4
					Next
				Next
				If n
					o[0]=r/n
					o[1]=g/n
					o[2]=b/n
				Else
					o[0]=0't[0]
					o[1]=0't[1]
					o[2]=0't[2]
				EndIf
				o[3]=0
			EndIf
			t:+4
			o:+4
		Next
	Next
	Return out
End Function

Rem
bbdoc: Flip a pixmap horizontally
returns: A new pixmap object
end rem
Function XFlipPixmap:TPixmap( pixmap:TPixmap ) NoDebug
	Local out:TPixmap=CreatePixmap( pixmap.width,pixmap.height,pixmap.format )
	For Local x:Int=0 Until pixmap.width
		out.Paste pixmap.Window(pixmap.width-x-1,0,1,pixmap.height),x,0
	Next
	Return out
End Function

Rem
bbdoc: Flip a pixmap vertically
returns: A new pixmap object
end rem
Function YFlipPixmap:TPixmap( pixmap:TPixmap ) NoDebug
	Local out:TPixmap=CreatePixmap( pixmap.width,pixmap.height,pixmap.format )
	For Local y:Int=0 Until pixmap.height
		out.paste pixmap.Window(0,pixmap.height-y-1,pixmap.width,1),0,y
	Next
	Return out
End Function

Rem
bbdoc: Resize a pixmap
returns: A new pixmap object of the specified @width and @height
end rem
Function ResizePixmap:TPixmap( pixmap:TPixmap,width:Int,height:Int ) NoDebug
	Local in_pixmap:TPixmap=pixmap
	If in_pixmap.format<>PF_STDFORMAT in_pixmap=pixmap.Convert( PF_STDFORMAT )
	Local tmp:Byte[width*4]
	Local x_sc:Float=Float(in_pixmap.width)/width
	Local y_sc:Float=Float(in_pixmap.height)/height
	Local out_pixmap:TPixmap=CreatePixmap( width,height,pixmap.format )
	For Local y:Int=0 Until height
		Local ty:Float=(y+.5)*y_sc-.5
		Local iy:Float=Floor(ty),fy#=ty-iy
		Local in_pitch:Int=in_pixmap.pitch
		If iy<0
			iy=0;fy=0;in_pitch=0
		Else If iy>=in_pixmap.height-1
			iy=in_pixmap.height-1;fy=0;in_pitch=0
		EndIf
		Local src:Byte Ptr=in_pixmap.PixelPtr(0,Int(iy)),dst:Byte Ptr=tmp
		For Local x:Int=0 Until width
			Local tx:Float=(x+.5)*x_sc-.5
			Local ix:Float=Floor(tx),fx#=tx-ix
			Local in_off:Int=4
			If ix<0
				ix=0;fx=0;in_off=0
			Else If ix>=in_pixmap.width-1
				ix=in_pixmap.width-1;fx=0;in_off=0
			EndIf
			Local p:Byte Ptr=src+Int(ix)*4
			For Local n:Int=0 Until 4
				Local v0:Float=p[n],v1#=p[n+in_off]
				Local v2:Float=p[n+in_pitch],v3#=p[n+in_pitch+in_off]
				Local va:Float=(v1-v0)*fx+v0,vb#=(v3-v2)*fx+v2,vt#=(vb-va)*fy+va
				dst[n]=vt
			Next
			dst:+4
		Next
		ConvertPixels tmp,PF_STDFORMAT,out_pixmap.Pixelptr(0,y),out_pixmap.format,width
	Next
	Return out_pixmap
End Function

Rem
bbdoc: Load a pixmap
returns: A pixmap object
end rem
Function LoadPixmap:TPixmap( url:Object )
	Local stream:TStream=ReadStream( url )
	If Not stream Return Null

	Local pos:Int=stream.Pos()
	If pos=-1
		stream.Close
		Return Null
	EndIf

	Local pixmap:TPixmap
	Local loader:TPixmapLoader=pixmap_loaders

	While loader
		stream.Seek( pos )
		Try
			pixmap=loader.LoadPixmap( stream )
		Catch ex:TStreamException
		End Try
		If pixmap Exit
		loader=loader._succ
	Wend
	stream.Close
	Return pixmap
End Function

Rem
bbdoc: Read a pixel from a pixmap
returns: A 32 bit pixel value
about:
The returned 32 bit value contains the following components:

[ bits 24-31 | pixel alpha
* bits 16-23 | pixel red
* bits 8-15 | pixel green
* bits 0-7 | pixel blue
]
End Rem
Function ReadPixel:Int( pixmap:TPixmap,x:Int,y:Int )
	Return pixmap.ReadPixel( x,y )
End Function

Function ReadPixelColor:SColor8( pixmap:TPixmap,x:Int,y:Int )
	Return pixmap.ReadPixelColor( x,y )
End Function

Rem
bbdoc: Write a pixel to a pixmap
about:
The 32 bit @argb value contains the following components:

[ bits 24-31 | pixel alpha
* bits 16-23 | pixel red
* bits 8-15 | pixel green
* bits 0-7 | pixel blue
]
End Rem
Function WritePixel( pixmap:TPixmap,x:Int,y:Int,argb:Int )
	pixmap.WritePixel x,y,argb
End Function

Function WritePixel( pixmap:TPixmap,x:Int,y:Int,color:SColor8 )
	pixmap.WritePixel x,y,color
End Function

Rem
bbdoc: Clear a pixmap
about:
Sets all pixels in a pixmap to a 32 bit pixel value.

The 32 bit @argb value contains the following components:

[ bits 24-31 | pixel alpha
* bits 16-23 | pixel red
* bits 8-15 | pixel green
* bits 0-7 | pixel blue
]
End Rem
Function ClearPixels( pixmap:TPixmap,argb:Int=0 )
	pixmap.ClearPixels argb
End Function

Function ClearPixels( pixmap:TPixmap,color:SColor8 )
	pixmap.ClearPixels color
End Function

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions