image.monkey2 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550
  1. Namespace mojo.graphics
  2. Using std.resource
  3. #rem monkeydoc The Image class.
  4. An image is a rectangular array of pixels that can be drawn to a canvas using one of the [[Canvas.DrawImage]] methods.
  5. Images are similar to pixmap's, except that they are optimized for rendering, and typically live in GPU memory.
  6. To load an image from a file, use one of the [[Load]], [[LoadBump]] or [[LoadLight]] functions.
  7. To create an image from an existing pixmap, use the New( pixmap,... ) constructor.
  8. To create an image that is a 'window' into an existing image, use the New( image,rect... ) constructor. This allows you to use images as 'atlases',
  9. To create an 'empty' image, use the New( width,height ) constructor. You can then render to this image by creating a canvas with this image as its render target.
  10. Images also have several properties that affect how they are rendered, including:
  11. * Handle - the relative position of the image's centre (or 'pivot point') for rendering, where (0.0,0.0) means the top-left of the image while (1.0,1.0) means the bottom-right.
  12. * Scale - a fixed scale factor for the image.
  13. * BlendMode - controls how the image is blended with the contents of the canvas. If this is null, this property is ignored and the current canvas blendmode is used to render the image instead.
  14. * Color - when rendering an image to a canvas, this property is multiplied by the current canvas color and the result is multiplied by actual image pixel colors to achieve the final color to be rendered.
  15. * TextureFilter - controls how image texels are sampled. Set to TextureFilter.None for coolio retro style graphics, or TextureFilter.Mipmap for fullon super smoothing.
  16. #end
  17. Class Image Extends Resource
  18. #rem monkeydoc Creates a new Image.
  19. New( pixmap,... ) Creates an image from an existing pixmap.
  20. New( width,height,... ) Creates an image that can be rendered to using a canvas.
  21. New( image,... ) Creates an image from within an 'atlas' image.
  22. Note: `textureFlags` should be null for static images or TextureFlags.Dynamic for dynamic images.
  23. @param pixmap Source image.
  24. @param textureFlags Image texture flags.
  25. @param shader Image shader.
  26. @param image Source pixmap.
  27. @param rect Source rect.
  28. @param x,y,width,height Source rect
  29. @param width,height Image size.
  30. #end
  31. Method New( pixmap:Pixmap,textureFlags:TextureFlags=Null,shader:Shader=Null )
  32. Local texture:=New Texture( pixmap,textureFlags )
  33. Init( texture,texture.Rect,shader )
  34. AddDependancy( texture )
  35. End
  36. Method New( width:Int,height:Int,textureFlags:TextureFlags=Null,shader:Shader=Null )
  37. Local texture:=New Texture( width,height,PixelFormat.RGBA32,textureFlags )
  38. Init( texture,texture.Rect,shader )
  39. AddDependancy( texture )
  40. End
  41. Method New( image:Image )
  42. Init( image._textures[0],image._rect,image._shader )
  43. image.AddDependancy( Self )
  44. For Local i:=1 Until 4
  45. SetTexture( i,image.GetTexture( i ) )
  46. Next
  47. BlendMode=image.BlendMode
  48. TextureFilter=image.TextureFilter
  49. LightDepth=image.LightDepth
  50. Handle=image.Handle
  51. Scale=image.Scale
  52. Color=image.Color
  53. End
  54. Method New( image:Image,rect:Recti )
  55. Init( image._textures[0],rect+image._rect.Origin,image._shader )
  56. image.AddDependancy( Self )
  57. For Local i:=1 Until 4
  58. SetTexture( i,image.GetTexture( i ) )
  59. Next
  60. BlendMode=image.BlendMode
  61. TextureFilter=image.TextureFilter
  62. LightDepth=image.LightDepth
  63. Handle=image.Handle
  64. Scale=image.Scale
  65. Color=image.Color
  66. End
  67. Method New( image:Image,x:Int,y:Int,width:Int,height:Int )
  68. Self.New( image,New Recti( x,y,x+width,y+height ) )
  69. End
  70. #rem monkeydoc @hidden
  71. #end
  72. Method New( texture:Texture,shader:Shader=Null )
  73. Init( texture,texture.Rect,shader )
  74. End
  75. #rem monkeydoc @hidden
  76. #end
  77. Method New( texture:Texture,rect:Recti,shader:Shader=Null )
  78. Init( texture,rect,shader )
  79. End
  80. #rem monkeydoc The image's primary texture.
  81. #end
  82. Property Texture:Texture()
  83. Return _textures[0]
  84. Setter( texture:Texture )
  85. SetTexture( 0,texture )
  86. End
  87. #rem monkeydoc The image's texture rect.
  88. Describes the rect the image occupies within its primary texture.
  89. #end
  90. Property Rect:Recti()
  91. Return _rect
  92. End
  93. #rem monkeydoc The image handle.
  94. Image handle values are fractional, where 0,0 is the top-left of the image and 1,1 is the bottom-right.
  95. #end
  96. Property Handle:Vec2f()
  97. Return _handle
  98. Setter( handle:Vec2f )
  99. _handle=handle
  100. UpdateVertices()
  101. End
  102. #rem monkeydoc The image scale.
  103. The scale property provides a simple way to 'pre-scale' an image.
  104. For images with a constant scale, Scaling an image this way is faster than using one of the 'scale' parameters of [[Canvas.DrawImage]].
  105. #end
  106. Property Scale:Vec2f()
  107. Return _scale
  108. Setter( scale:Vec2f )
  109. _scale=scale
  110. UpdateVertices()
  111. End
  112. #rem monkeydoc The image blend mode.
  113. The blend mode used to draw the image.
  114. If set to BlendMode.None, the canvas blend mode is used instead.
  115. Defaults to BlendMode.None.
  116. #end
  117. Property BlendMode:BlendMode()
  118. Return _blendMode
  119. Setter( blendMode:BlendMode )
  120. _blendMode=blendMode
  121. End
  122. #rem monkeydoc The image texture filter.
  123. The texture flags used to draw the image.
  124. If set to TextureFilter.None, the canvas texture filter is used instead.
  125. Defaults to TextureFilter.None
  126. #end
  127. Property TextureFilter:TextureFilter()
  128. Return _textureFilter
  129. Setter( filter:TextureFilter )
  130. _textureFilter=filter
  131. End
  132. #rem monkeydoc The image color.
  133. The color used to draw the image.
  134. Image color is multiplied by canvas color to achieve the final rendering color.
  135. Defaults to white.
  136. #end
  137. Property Color:Color()
  138. Return _color
  139. Setter( color:Color )
  140. _color=color
  141. _material.SetVector( "mx2_ImageColor",_color )
  142. End
  143. #rem monkeydoc The image light depth.
  144. #end
  145. Property LightDepth:Float()
  146. Return _lightDepth
  147. Setter( depth:Float )
  148. _lightDepth=depth
  149. _material.SetScalar( "mx2_LightDepth",_lightDepth )
  150. End
  151. #rem monkeydoc Shadow caster attached to image.
  152. #end
  153. Property ShadowCaster:ShadowCaster()
  154. Return _shadowCaster
  155. Setter( shadowCaster:ShadowCaster )
  156. _shadowCaster=shadowCaster
  157. End
  158. #rem monkeydoc The image bounds.
  159. The bounds rect represents the actual image vertices used when the image is drawn.
  160. Image bounds are affected by [[Scale]] and [[Handle]], and can be used for simple collision detection.
  161. #end
  162. Property Bounds:Rectf()
  163. Return _bounds
  164. End
  165. #rem monkeydoc Image bounds width.
  166. #end
  167. Property Width:Float()
  168. Return _bounds.Width
  169. End
  170. #rem monkeydoc Image bounds height.
  171. #end
  172. Property Height:Float()
  173. Return _bounds.Height
  174. End
  175. #rem monkeydoc Image bounds radius.
  176. #end
  177. Property Radius:Float()
  178. Return _radius
  179. End
  180. #rem monkeydoc Image shader.
  181. #end
  182. Property Shader:Shader()
  183. Return _shader
  184. End
  185. #rem monkeydoc Image material.
  186. #end
  187. Property Material:UniformBlock()
  188. Return _material
  189. End
  190. #rem monkeydoc @hidden Image vertices.
  191. #end
  192. Property Vertices:Rectf()
  193. Return _vertices
  194. End
  195. #rem monkeydoc @hidden Image texture coorinates.
  196. #end
  197. Property TexCoords:Rectf()
  198. Return _texCoords
  199. End
  200. #rem monkeydoc @hidden Sets an image texture.
  201. #end
  202. Method SetTexture( index:Int,texture:Texture )
  203. _textures[index]=texture
  204. _material.SetTexture( "mx2_ImageTexture"+index,texture )
  205. End
  206. #rem monkeydoc @hidden gets an image texture.
  207. #end
  208. Method GetTexture:Texture( index:Int )
  209. Return _textures[index]
  210. End
  211. #rem monkeydoc Loads an image from file.
  212. #end
  213. Function Load:Image( path:String,shader:Shader=Null )
  214. Local pixmap:=Pixmap.Load( path,Null,True )
  215. If Not pixmap Return Null
  216. If Not shader shader=mojo.graphics.Shader.GetShader( "sprite" )
  217. Local image:=New Image( pixmap,Null,shader )
  218. image.OnDiscarded+=Lambda()
  219. pixmap.Discard()
  220. End
  221. Return image
  222. End
  223. #rem monkeydoc Loads a bump image from file(s).
  224. `diffuse`, `normal` and `specular` are filepaths of the diffuse, normal and specular image files respectively.
  225. `specular` can be null, in which case `specularScale` is used for the specular component. Otherwise, `specularScale` is used to modulate the red component of the specular texture.
  226. #end
  227. Function LoadBump:Image( diffuse:String,normal:String,specular:String,specularScale:Float=1,flipNormalY:Bool=True,shader:Shader=Null )
  228. Local texture1:=graphics.Texture.LoadNormal( normal,Null,specular,specularScale,flipNormalY )
  229. If Not texture1 Return Null
  230. Local texture0:=graphics.Texture.Load( diffuse,Null )
  231. If Not texture0
  232. Local pdiff:=New Pixmap( texture1.Width,texture1.Height,PixelFormat.I8 )
  233. pdiff.Clear( std.graphics.Color.White )
  234. texture0=New graphics.Texture( pdiff,Null )
  235. Endif
  236. If Not shader shader=graphics.Shader.GetShader( "bump" )
  237. Local image:=New Image( texture0,texture0.Rect,shader )
  238. image.SetTexture( 1,texture1 )
  239. image.OnDiscarded+=Lambda()
  240. If texture0 texture0.Discard()
  241. If texture1 texture1.Discard()
  242. End
  243. Return image
  244. End
  245. #rem monkeydoc Loads a light image from file.
  246. #end
  247. Function LoadLight:Image( path:String,shader:Shader=Null )
  248. Local pixmap:=Pixmap.Load( path )
  249. If Not pixmap Return Null
  250. If Not shader shader=mojo.graphics.Shader.GetShader( "light" )
  251. Select pixmap.Format
  252. Case PixelFormat.IA16,PixelFormat.RGBA32
  253. pixmap.PremultiplyAlpha()
  254. Case PixelFormat.A8
  255. Local tpixmap:=pixmap
  256. pixmap=pixmap.Convert( PixelFormat.IA16 )
  257. tpixmap.Discard()
  258. 'Copy A->I
  259. For Local y:=0 Until pixmap.Height
  260. Local p:=pixmap.PixelPtr( 0,y )
  261. For Local x:=0 Until pixmap.Width
  262. p[0]=p[1]
  263. p+=2
  264. Next
  265. Next
  266. Case PixelFormat.I8
  267. Local tpixmap:=pixmap
  268. pixmap=pixmap.Convert( PixelFormat.IA16 )
  269. tpixmap.Discard()
  270. 'Copy I->A
  271. For Local y:=0 Until pixmap.Height
  272. Local p:=pixmap.PixelPtr( 0,y )
  273. For Local x:=0 Until pixmap.Width
  274. p[1]=p[0]
  275. p+=2
  276. Next
  277. Next
  278. Case PixelFormat.RGB24
  279. Local tpixmap:=pixmap
  280. pixmap=pixmap.Convert( PixelFormat.RGBA32 )
  281. tpixmap.Discard()
  282. 'Copy Max(R,G,B)->A
  283. For Local y:=0 Until pixmap.Height
  284. Local p:=pixmap.PixelPtr( 0,y )
  285. For Local x:=0 Until pixmap.Width
  286. p[3]=Max( Max( p[0],p[1] ),p[2] )
  287. p+=4
  288. Next
  289. Next
  290. End
  291. Local texture:=New Texture( pixmap,Null )
  292. Local image:=New Image( texture,shader )
  293. image.OnDiscarded+=Lambda()
  294. pixmap.Discard()
  295. End
  296. Return image
  297. End
  298. Private
  299. Field _shader:Shader
  300. Field _material:UniformBlock
  301. Field _textures:=New Texture[4]
  302. Field _blendMode:BlendMode
  303. Field _textureFilter:TextureFilter
  304. Field _color:Color
  305. Field _lightDepth:Float
  306. Field _shadowCaster:ShadowCaster
  307. Field _rect:Recti
  308. Field _handle:Vec2f
  309. Field _scale:Vec2f
  310. Field _vertices:Rectf
  311. Field _texCoords:Rectf
  312. Field _bounds:Rectf
  313. Field _radius:Float
  314. Method Init( texture:Texture,rect:Recti,shader:Shader )
  315. If Not shader shader=Shader.GetShader( "sprite" )
  316. _rect=rect
  317. _shader=shader
  318. _material=New UniformBlock
  319. AddDependancy( _material )
  320. SetTexture( 0,texture )
  321. BlendMode=BlendMode.None
  322. TextureFilter=TextureFilter.None
  323. Color=Color.White
  324. LightDepth=100
  325. Handle=New Vec2f( 0 )
  326. Scale=New Vec2f( 1 )
  327. UpdateVertices()
  328. UpdateTexCoords()
  329. End
  330. Method UpdateVertices()
  331. _vertices.min.x=Float(_rect.Width)*(0-_handle.x)*_scale.x
  332. _vertices.min.y=Float(_rect.Height)*(0-_handle.y)*_scale.y
  333. _vertices.max.x=Float(_rect.Width)*(1-_handle.x)*_scale.x
  334. _vertices.max.y=Float(_rect.Height)*(1-_handle.y)*_scale.y
  335. _bounds.min.x=Min( _vertices.min.x,_vertices.max.x )
  336. _bounds.max.x=Max( _vertices.min.x,_vertices.max.x )
  337. _bounds.min.y=Min( _vertices.min.y,_vertices.max.y )
  338. _bounds.max.y=Max( _vertices.min.y,_vertices.max.y )
  339. _radius=_bounds.min.x*_bounds.min.x+_bounds.min.y*_bounds.min.y
  340. _radius=Max( _radius,_bounds.max.x*_bounds.max.x+_bounds.min.y*_bounds.min.y )
  341. _radius=Max( _radius,_bounds.max.x*_bounds.max.x+_bounds.max.y*_bounds.max.y )
  342. _radius=Max( _radius,_bounds.min.x*_bounds.min.x+_bounds.max.y*_bounds.max.y )
  343. _radius=Sqrt( _radius )
  344. End
  345. Method UpdateTexCoords()
  346. _texCoords.min.x=Float(_rect.min.x)/_textures[0].Width
  347. _texCoords.min.y=Float(_rect.min.y)/_textures[0].Height
  348. _texCoords.max.x=Float(_rect.max.x)/_textures[0].Width
  349. _texCoords.max.y=Float(_rect.max.y)/_textures[0].Height
  350. End
  351. End
  352. Class ResourceManager Extension
  353. Method OpenImage:Image( path:String,shader:Shader=Null )
  354. Local slug:="Image:name="+StripDir( StripExt( path ) )+"&shader="+(shader ? shader.Name Else "null")
  355. Local image:=Cast<Image>( OpenResource( slug ) )
  356. If image Return image
  357. Local texture:=OpenTexture( path,Null )
  358. If texture image=New Image( texture,shader )
  359. AddResource( slug,image )
  360. Return image
  361. End
  362. End