pixmap.monkey2 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681
  1. Namespace std.graphics
  2. Using std.resource
  3. Extern Private
  4. Function ldexp:Float( x:Float,exp:Int )
  5. Function frexp:Float( arg:Float,exp:Int Ptr )
  6. Private
  7. Function GetColorRGBE8:Color( p:UByte Ptr)
  8. If Not p[3] Return Color.Black
  9. Local f:=ldexp( 1.0,p[3]-136 )
  10. Return New Color( p[0]*f,p[1]*f,p[2]*f )
  11. End
  12. Function SetColorRGBE8( p:UByte Ptr,color:Color )
  13. Local v:=color.r,e:=0
  14. If color.g>v v=color.g
  15. If color.b>v v=color.b
  16. If( v<1e-32) p[0]=0;p[1]=0;p[2]=0;p[3]=0;Return
  17. v=frexp( v,Varptr e ) * 256.0/v
  18. p[0]=color.r*v
  19. p[1]=color.g*v
  20. p[2]=color.b*v
  21. p[0]=e+128
  22. End
  23. Public
  24. #rem monkeydoc Pixmaps allow you to store and manipulate rectangular blocks of pixel data.
  25. A pixmap contains a block of memory used to store a rectangular array of pixels.
  26. #end
  27. Class Pixmap Extends Resource
  28. #rem monkeydoc Creates a new pixmap.
  29. When you have finished with the pixmap, you should call its inherited [[resource.Resource.Discard]] method.
  30. @param width The width of the pixmap in pixels.
  31. @param height The height of the pixmap in pixels.
  32. @param format The pixmap format.
  33. @param data A pointer to the pixmap data.
  34. @param pitch The pitch of the data.
  35. #end
  36. Method New( width:Int,height:Int,format:PixelFormat=PixelFormat.RGBA8 )
  37. ' Print "New pixmap1, width="+width+", height="+height
  38. Local depth:=PixelFormatDepth( format )
  39. Local pitch:=width*depth
  40. Local data:=Cast<UByte Ptr>( GCMalloc( pitch*height ) )
  41. _width=width
  42. _height=height
  43. _format=format
  44. _depth=depth
  45. _pitch=pitch
  46. _owned=True
  47. _data=data
  48. End
  49. Method New( width:Int,height:Int,format:PixelFormat,data:UByte Ptr,pitch:Int )
  50. ' Print "New pixmap2, width="+width+", height="+height
  51. Local depth:=PixelFormatDepth( format )
  52. _width=width
  53. _height=height
  54. _format=format
  55. _depth=depth
  56. _data=data
  57. _pitch=pitch
  58. End
  59. #rem monkeydoc The width and height of the pixmap.
  60. #end
  61. Property Size:Vec2i()
  62. Return New Vec2i( _width,_height )
  63. End
  64. #rem monkeydoc The pixmap width.
  65. #end
  66. Property Width:Int()
  67. Return _width
  68. End
  69. #rem monkeydoc The pixmap height.
  70. #end
  71. Property Height:Int()
  72. Return _height
  73. End
  74. #rem monkeydoc The pixmap format.
  75. #end
  76. Property Format:PixelFormat()
  77. Return _format
  78. End
  79. #rem monkeydoc The pixmap depth.
  80. The number of bytes per pixel.
  81. #end
  82. Property Depth:Int()
  83. Return _depth
  84. End
  85. #rem monkeydoc True if pixmap format includes alpha.
  86. #end
  87. Property HasAlpha:Bool()
  88. Select _format
  89. Case PixelFormat.A8,PixelFormat.IA8,PixelFormat.RGBA8
  90. Return True
  91. End
  92. Return False
  93. End
  94. #rem monkeydoc The raw pixmap data.
  95. #end
  96. Property Data:UByte Ptr()
  97. Return _data
  98. End
  99. #rem monkeydoc The pixmap pitch.
  100. This is the number of bytes between one row of pixels in the pixmap and the next.
  101. #end
  102. Property Pitch:Int()
  103. Return _pitch
  104. End
  105. #rem monkeydoc Gets a pointer to a pixel in the pixmap.
  106. @param x the x coordinate of the pixel.
  107. @param y the y coordinate of the pixel.
  108. @return the address of the pixel at `x`, `y`.
  109. #end
  110. Method PixelPtr:UByte Ptr( x:Int,y:Int )
  111. Return _data + y*_pitch + x*_depth
  112. End
  113. #rem monkeydoc Sets a pixel to a color.
  114. Sets the pixel at `x`, `y` to `pixel`.
  115. In debug builds, a runtime error will occur if the pixel coordinates lie outside of the pixmap area.
  116. @param x The x coordinate of the pixel.
  117. @param y The y coordinate of the pixel.
  118. @param color The color to set the pixel to.
  119. #end
  120. Method SetPixel( x:Int,y:Int,color:Color )
  121. DebugAssert( x>=0 And y>=0 And x<_width And y<_height,"Pixmap pixel coordinates out of range" )
  122. Local p:=PixelPtr( x,y )
  123. Select _format
  124. Case PixelFormat.A8
  125. p[0]=color.a * 255
  126. Case PixelFormat.I8
  127. p[0]=color.r * 255
  128. Case PixelFormat.IA8
  129. p[0]=color.r * 255
  130. p[1]=color.a * 255
  131. Case PixelFormat.RGB8
  132. p[0]=color.r * 255
  133. p[1]=color.g * 255
  134. p[2]=color.b * 255
  135. Case PixelFormat.RGBA8
  136. p[0]=color.r * 255
  137. p[1]=color.g * 255
  138. p[2]=color.b * 255
  139. p[3]=color.a * 255
  140. Case PixelFormat.RGB32F
  141. Local f:=Cast<Float Ptr>( p )
  142. f[0]=color.r
  143. f[1]=color.g
  144. f[2]=color.b
  145. Case PixelFormat.RGBA32F
  146. Local f:=Cast<Float Ptr>( p )
  147. f[0]=color.r
  148. f[1]=color.g
  149. f[2]=color.b
  150. f[3]=color.a
  151. Case PixelFormat.RGBE8
  152. SetColorRGBE8( p,color )
  153. Default
  154. Assert( False )
  155. End
  156. End
  157. #rem monkeydoc Gets the color of a pixel.
  158. Gets the pixel at `x`, `y` and returns it in ARGB format.
  159. In debug builds, a runtime error will occur if the pixel coordinates lie outside of the pixmap area.
  160. @param x The x coordinate of the pixel.
  161. @param y The y coordinate of the pixel.
  162. @return The color of the pixel at `x`, `y`.
  163. #end
  164. Method GetPixel:Color( x:Int,y:Int )
  165. DebugAssert( x>=0 And y>=0 And x<_width And y<_height,"Pixmap pixel coordinates out of range" )
  166. Local p:=PixelPtr( x,y )
  167. Select _format
  168. Case PixelFormat.A8
  169. Return New Color( 0,0,0,p[0]/255.0 )
  170. Case PixelFormat.I8
  171. Local i:=p[0]/255.0
  172. Return New Color( i,i,i,1 )
  173. Case PixelFormat.IA8
  174. Local i:=p[0]/255.0
  175. Return New Color( i,i,i,p[1]/255.0 )
  176. Case PixelFormat.RGB8
  177. Return New Color( p[0]/255.0,p[1]/255.0,p[2]/255.0,1 )
  178. Case PixelFormat.RGBA8
  179. Return New Color( p[0]/255.0,p[1]/255.0,p[2]/255.0,p[3]/255.0 )
  180. Case PixelFormat.RGB32F
  181. Local f:=Cast<Float Ptr>( p )
  182. Return New Color( f[0],f[1],f[2] )
  183. Case PixelFormat.RGBA32F
  184. Local f:=Cast<Float Ptr>( p )
  185. Return New Color( f[0],f[1],f[2],f[3] )
  186. Case PixelFormat.RGBE8
  187. Return GetColorRGBE8( p )
  188. Default
  189. Assert( False )
  190. End
  191. Return Color.None
  192. End
  193. #rem monkeydoc Sets a pixel to an ARGB color.
  194. Sets the pixel at `x`, `y` to `pixel`.
  195. In debug builds, a runtime error will occur if the pixel coordinates lie outside of the pixmap area.
  196. @param x The x coordinate of the pixel.
  197. @param y The y coordinate of the pixel.
  198. @param color The pixel to set in ARGB format.
  199. #end
  200. Method SetPixelARGB( x:Int,y:Int,color:UInt )
  201. DebugAssert( x>=0 And y>=0 And x<_width And y<_height,"Pixmap pixel coordinates out of range" )
  202. Local p:=PixelPtr( x,y )
  203. Select _format
  204. Case PixelFormat.A8
  205. p[0]=color Shr 24
  206. Case PixelFormat.I8
  207. p[0]=color Shr 16
  208. Case PixelFormat.IA8
  209. p[0]=color Shr 24
  210. p[1]=color Shr 16
  211. Case PixelFormat.RGB8
  212. p[0]=color Shr 16
  213. p[1]=color Shr 8
  214. p[2]=color
  215. Case PixelFormat.RGBA8
  216. p[0]=color Shr 16
  217. p[1]=color Shr 8
  218. p[2]=color
  219. p[3]=color Shr 24
  220. Case PixelFormat.RGB32F
  221. Local f:=Cast<Float Ptr>( p )
  222. f[0]=((color Shr 16)&255)/255.0
  223. f[1]=((color Shr 8)&255)/255.0
  224. f[2]=(color&255)/255.0
  225. Case PixelFormat.RGBA32F
  226. Local f:=Cast<Float Ptr>( p )
  227. f[0]=((color Shr 16)&255)/255.0
  228. f[1]=((color Shr 8)&255)/255.0
  229. f[2]=(color&255)/255.0
  230. f[3]=((color Shr 24)&255)/255.0
  231. Case PixelFormat.RGBE8
  232. SetColorRGBE8( p,New Color( ((color Shr 16)&255)/255.0,((color Shr 8)&255)/255.0,(color&255)/255.0,((color Shr 24)&255)/255.0 ) )
  233. Default
  234. SetPixel( x,y,New Color( ((color Shr 16)&255)/255.0,((color Shr 8)&255)/255.0,(color&255)/255.0,((color Shr 24)&255)/255.0 ) )
  235. End
  236. End
  237. #rem monkeydoc Gets the ARGB color of a pixel.
  238. Get the pixel at `x`, `y` and returns it in ARGB format.
  239. @param x the x coordinate of the pixel.
  240. @param y the y coordinate of the pixel.
  241. @return the pixel at `x`, `y` in ARGB format.
  242. #end
  243. Method GetPixelARGB:UInt( x:Int,y:Int )
  244. DebugAssert( x>=0 And y>=0 And x<_width And y<_height,"Pixmap pixel coordinates out of range" )
  245. Local p:=PixelPtr( x,y )
  246. Select _format
  247. Case PixelFormat.A8
  248. Return p[0] Shl 24
  249. Case PixelFormat.I8
  250. Local i:=p[0]
  251. Return UByte($ff) Shl 24 | i Shl 16 | i Shl 8 | i
  252. Case PixelFormat.IA8
  253. Local i:=p[1]
  254. Return p[0] Shl 24 | i Shl 16 | i Shl 8 | i
  255. Case PixelFormat.RGB8
  256. Return UByte($ff) Shl 24 | p[0] Shl 16 | p[1] Shl 8 | p[2]
  257. Case PixelFormat.RGBA8
  258. Return p[3] Shl 24 | p[0] Shl 16 | p[1] Shl 8 | p[2]
  259. Case PixelFormat.RGB32F
  260. Local f:=Cast<Float Ptr>( p )
  261. Return UInt($ff) Shl 24 | UInt(f[0]*255.0) Shl 16 | UInt(f[1]*255.0) Shl 8 | UInt(f[2]*255.0)
  262. Case PixelFormat.RGBA32F
  263. Local f:=Cast<Float Ptr>( p )
  264. Return UInt(f[3]*255.0) Shl 24 | UInt(f[0]*255.0) Shl 16 | UInt(f[1]*255.0) Shl 8 | UInt(f[2]*255.0)
  265. Case PixelFormat.RGBE8
  266. Local color:=GetColorRGBE8( p )
  267. Return UInt($ff) Shl 24 | UInt(color.r*255.0) Shl 16 | UInt(color.g*255.0) Shl 8 | UInt(color.b*255.0)
  268. Default
  269. Local color:=GetPixel( x,y )
  270. Return UInt(color.a*255.0) Shl 24 | UInt(color.r*255.0) Shl 16 | UInt(color.g*255.0) Shl 8 | UInt(color.b*255.0)
  271. End
  272. Return 0
  273. End
  274. 'Optimize!
  275. '
  276. #rem monkeydoc Clears the pixmap to a given color.
  277. @param color The color to clear the pixmap to.
  278. #end
  279. Method Clear( color:Color )
  280. For Local y:=0 Until _height
  281. For Local x:=0 Until _width
  282. SetPixel( x,y,color )
  283. Next
  284. Next
  285. End
  286. #rem monkeydoc Clears the pixmap to an ARGB color.
  287. @param color ARGB color to clear the pixmap to.
  288. #end
  289. Method ClearARGB( color:UInt )
  290. For Local y:=0 Until _height
  291. For Local x:=0 Until _width
  292. SetPixelARGB( x,y,color )
  293. Next
  294. Next
  295. End
  296. #rem monkeydoc Creates a copy of the pixmap.
  297. @return A new pixmap.
  298. #end
  299. Method Copy:Pixmap()
  300. Local pitch:=Width * Depth
  301. Local data:=Cast<UByte Ptr>( libc.malloc( pitch * Height ) )
  302. For Local y:=0 Until Height
  303. memcpy( data+y*pitch,PixelPtr( 0,y ),pitch )
  304. Next
  305. Return New Pixmap( Width,Height,Format,data,pitch )
  306. End
  307. #rem monkeydoc Paste a pixmap to the pixmap.
  308. In debug builds, a runtime error will occur if the operation would write to pixels outside of the pixmap.
  309. Note: No alpha blending is performed - pixels in the pixmap are simply overwritten.
  310. @param pixmap The pixmap to paste.
  311. @param x The x coordinate.
  312. @param y The y coordinate.
  313. #end
  314. Method Paste( pixmap:Pixmap,x:Int,y:Int )
  315. DebugAssert( x>=0 And x+pixmap._width<=_width And y>=0 And y+pixmap._height<=_height )
  316. For Local ty:=0 Until pixmap._height
  317. For Local tx:=0 Until pixmap._width
  318. SetPixel( x+tx,y+ty,pixmap.GetPixel( tx,ty ) )
  319. Next
  320. Next
  321. End
  322. 'Optimize!
  323. '
  324. #rem monkeydoc Converts the pixmap to a different format.
  325. @param format The pixel format to convert the pixmap to.
  326. @return A new pixmap.
  327. #end
  328. Method Convert:Pixmap( format:PixelFormat )
  329. Local t:=New Pixmap( _width,_height,format )
  330. If IsFloatPixelFormat( _format ) And Not IsFloatPixelFormat( format )
  331. For Local y:=0 Until _height
  332. For Local x:=0 Until _width
  333. Local c:=GetPixel( x,y )
  334. c.r=Clamp( c.r,0.0,1.0 )
  335. c.g=Clamp( c.g,0.0,1.0 )
  336. c.b=Clamp( c.b,0.0,1.0 )
  337. c.a=Clamp( c.a,0.0,1.0 )
  338. t.SetPixel( x,y,c )
  339. Next
  340. Next
  341. Else
  342. For Local y:=0 Until _height
  343. For Local x:=0 Until _width
  344. t.SetPixel( x,y,GetPixel( x,y ) )
  345. Next
  346. Next
  347. Endif
  348. Return t
  349. End
  350. 'Optimize!
  351. '
  352. #rem monkeydoc Premultiply pixmap r,g,b components by alpha.
  353. #end
  354. Method PremultiplyAlpha()
  355. Select _format
  356. Case PixelFormat.IA8,PixelFormat.RGBA8,PixelFormat.RGBA16F,PixelFormat.RGBA32F
  357. For Local y:=0 Until _height
  358. For Local x:=0 Until _width
  359. Local color:=GetPixel( x,y )
  360. color.r*=color.a
  361. color.g*=color.a
  362. color.b*=color.a
  363. SetPixel( x,y,color )
  364. Next
  365. Next
  366. End
  367. End
  368. #rem monkeydoc @hidden Halves the pixmap for mipmapping
  369. Mipmap must be even width and height.
  370. FIXME: Handle funky sizes.
  371. #end
  372. Method MipHalve:Pixmap()
  373. If Width=1 And Height=1 Return Null
  374. Local dst:=New Pixmap( Max( Width/2,1 ),Max( Height/2,1 ),Format )
  375. If Width=1
  376. For Local y:=0 Until dst.Height
  377. Local c0:=GetPixel( 0,y*2 )
  378. Local c1:=GetPixel( 0,y*2+1 )
  379. dst.SetPixel( 0,y,(c0+c1)*0.5 )
  380. Next
  381. Return dst
  382. Else If Height=1
  383. For Local x:=0 Until dst.Width
  384. Local c0:=GetPixel( x*2,0 )
  385. Local c1:=GetPixel( x*2+1,0 )
  386. dst.SetPixel( x,0,(c0+c1)*0.5 )
  387. Next
  388. Return dst
  389. Endif
  390. Select _format
  391. Case PixelFormat.RGBA8
  392. For Local y:=0 Until dst.Height
  393. Local dstp:=Cast<UInt Ptr>( dst.PixelPtr( 0,y ) )
  394. Local srcp0:=Cast<UInt Ptr>( PixelPtr( 0,y*2 ) )
  395. Local srcp1:=Cast<UInt Ptr>( PixelPtr( 0,y*2+1 ) )
  396. For Local x:=0 Until dst.Width
  397. Local src0:=srcp0[0],src1:=srcp0[1],src2:=srcp1[0],src3:=srcp1[1]
  398. Local dst:=( (src0 Shr 2)+(src1 Shr 2)+(src2 Shr 2)+(src3 Shr 2) ) & $ff000000
  399. dst|=( (src0 & $ff0000)+(src1 & $ff0000)+(src2 & $ff0000)+(src3 & $ff0000) ) Shr 2 & $ff0000
  400. dst|=( (src0 & $ff00)+(src1 & $ff00)+(src2 & $ff00)+(src3 & $ff00) ) Shr 2 & $ff00
  401. dst|=( (src0 & $ff)+(src1 & $ff)+(src2 & $ff)+(src3 & $ff) ) Shr 2
  402. dstp[x]=dst
  403. srcp0+=2
  404. srcp1+=2
  405. Next
  406. Next
  407. Default
  408. For Local y:=0 Until dst.Height
  409. For Local x:=0 Until dst.Width
  410. Local c0:=GetPixel( x*2,y*2 )
  411. Local c1:=GetPixel( x*2+1,y*2 )
  412. Local c2:=GetPixel( x*2+1,y*2+1 )
  413. Local c3:=GetPixel( x*2,y*2+1 )
  414. Local cm:=(c0+c1+c2+c3)*.25
  415. dst.SetPixel( x,y,cm )
  416. Next
  417. Next
  418. End
  419. Return dst
  420. End
  421. #rem monkeydoc Flips the pixmap on the Y axis.
  422. #end
  423. Method FlipY()
  424. Local sz:=Width*Depth
  425. Local tmp:=New UByte[sz]
  426. For Local y:=0 Until Height/2
  427. Local p1:=PixelPtr( 0,y )
  428. Local p2:=PixelPtr( 0,Height-1-y )
  429. libc.memcpy( tmp.Data,p1,sz )
  430. libc.memcpy( p1,p2,sz )
  431. libc.memcpy( p2,tmp.Data,sz )
  432. Next
  433. End
  434. #rem monkeydoc Returns a rectangular window into the pixmap.
  435. In debug builds, a runtime error will occur if the rectangle lies outside of the pixmap area.
  436. @param x The x coordinate of the top left of the rectangle.
  437. @param y The y coordinate of the top left of the rectangle.
  438. @param width The width of the rectangle.
  439. @param height The height of the rectangle.
  440. #end
  441. Method Window:Pixmap( x:Int,y:Int,width:Int,height:Int )
  442. DebugAssert( x>=0 And y>=0 And width>=0 And height>=0 And x+width<=_width And y+height<=_height )
  443. Local pixmap:=New Pixmap( width,height,_format,PixelPtr( x,y ),_pitch )
  444. Return pixmap
  445. End
  446. #rem monkeydoc Saves the pixmap to a file.
  447. The only save format currently suppoted is PNG.
  448. #End
  449. Method Save:Bool( path:String )
  450. Return SavePixmap( Self,path )
  451. End
  452. #rem monkeydoc Loads a pixmap from a file.
  453. @param path The file path.
  454. @param format The format to load the pixmap in.
  455. @return Null if the file could not be opened, or contained invalid image data.
  456. #end
  457. Function Load:Pixmap( path:String,format:PixelFormat=Null,pmAlpha:Bool=False )
  458. Local pixmap:=pixmaploader.LoadPixmap( path,format )
  459. If Not pixmap And Not ExtractRootDir( path ) pixmap=pixmaploader.LoadPixmap( "image::"+path,format )
  460. If pixmap And pmAlpha pixmap.PremultiplyAlpha()
  461. Return pixmap
  462. End
  463. Protected
  464. #rem monkeydoc @hidden
  465. #end
  466. Method OnDiscard() Override
  467. If _owned GCFree( _data )
  468. _data=Null
  469. End
  470. #rem monkeydoc @hidden
  471. #end
  472. Method OnFinalize() Override
  473. If _owned GCFree( _data )
  474. End
  475. Private
  476. Field _width:Int
  477. Field _height:Int
  478. Field _format:PixelFormat
  479. Field _depth:Int
  480. Field _pitch:Int
  481. Field _owned:Bool
  482. Field _data:UByte Ptr
  483. End
  484. Class ResourceManager Extension
  485. Method OpenPixmap:Pixmap( path:String,format:PixelFormat=Null,pmAlpha:Bool=False )
  486. Local slug:="Pixmap:name="+StripDir( StripExt( path ) )+"&format="+Int( format )+"&pmAlpha="+Int( pmAlpha )
  487. Local pixmap:=Cast<Pixmap>( OpenResource( slug ) )
  488. If pixmap Return pixmap
  489. pixmap=Pixmap.Load( path,format,pmAlpha )
  490. If Not pixmap Return Null
  491. AddResource( slug,pixmap )
  492. Return pixmap
  493. End
  494. End