image.monkey2 12 KB

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