ImagingSDL.pas 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394
  1. {
  2. $Id$
  3. Vampyre Imaging Library
  4. by Marek Mauder
  5. http://imaginglib.sourceforge.net
  6. The contents of this file are used with permission, subject to the Mozilla
  7. Public License Version 1.1 (the "License"); you may not use this file except
  8. in compliance with the License. You may obtain a copy of the License at
  9. http://www.mozilla.org/MPL/MPL-1.1.html
  10. Software distributed under the License is distributed on an "AS IS" basis,
  11. WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
  12. the specific language governing rights and limitations under the License.
  13. Alternatively, the contents of this file may be used under the terms of the
  14. GNU Lesser General Public License (the "LGPL License"), in which case the
  15. provisions of the LGPL License are applicable instead of those above.
  16. If you wish to allow use of your version of this file only under the terms
  17. of the LGPL License and not to allow others to use your version of this file
  18. under the MPL, indicate your decision by deleting the provisions above and
  19. replace them with the notice and other provisions required by the LGPL
  20. License. If you do not delete the provisions above, a recipient may use
  21. your version of this file under either the MPL or the LGPL License.
  22. For more information about the LGPL: http://www.gnu.org/copyleft/lesser.html
  23. }
  24. { This unit contains functions for loading/saving SDL surfaces using Imaging
  25. and for converting images to surfaces and vice versa.}
  26. unit ImagingSDL;
  27. {$I ImagingOptions.inc}
  28. interface
  29. uses
  30. Classes, sdl, ImagingTypes, Imaging, ImagingUtility;
  31. type
  32. { This SDL type is redefined here so ImagingExport unit does not
  33. need sdl unit in the uses list.}
  34. PSDL_Surface = sdl.PSDL_Surface;
  35. { LoadSDLSurfaceFromFile and similar functions use SDL_SWSURFACE as Flags when creating
  36. SDL surface. If you want other Flags to be used load image by standard
  37. LoadImageFromFile and similar functions and then call CreateSDLSurfaceFromImage
  38. which has more options.}
  39. { Creates SDL surface from image in file in format supported by Imaging.}
  40. function LoadSDLSurfaceFromFile(const FileName: string): PSDL_Surface;
  41. { Creates SDL surface from image in stream in format supported by Imaging.}
  42. function LoadSDLSurfaceFromStream(Stream: TStream): PSDL_Surface;
  43. { Creates SDL surface from image in memory in format supported by Imaging.}
  44. function LoadSDLSurfaceFromMemory(Data: Pointer; Size: LongInt): PSDL_Surface;
  45. { Converts image to SDL surface. Flags is used when creating SDL surface
  46. using SDL_CreateRGBSurface and is passed to it. OverrideFormat can be
  47. used to convert image to specified format before SDL surface is created,
  48. ifUnknown means no conversion.}
  49. function CreateSDLSurfaceFromImage(const ImageData: TImageData;
  50. Flags: LongWord; OverrideFormat: TImageFormat = ifUnknown): PSDL_Surface;
  51. { Saves SDL surface to file in one of the formats supported by Imaging.}
  52. function SaveSDLSurfaceToFile(const FileName: string; Surface: PSDL_Surface): Boolean;
  53. { Saves SDL surface to stream in one of the formats supported by Imaging defined by Ext.}
  54. function SaveSDLSurfaceToStream(const Ext: string; Stream: TStream; Surface: PSDL_Surface): Boolean;
  55. { Saves SDL surface to memory in one of the formats supported by Imaging defined
  56. by Ext. Size must contain size of available memory before the function
  57. is called and memory size taken up by the image is returned in this parameter.}
  58. function SaveSDLSurfaceToMemory(const Ext: string; Data: Pointer; var Size: LongInt; Surface: PSDL_Surface): Boolean;
  59. { Converts SDL surface to TImageData structure. OverrideFormat can be
  60. used to convert output image to the specified format rather than
  61. use the format taken from SDL surface, ifUnknown means no conversion.}
  62. function CreateImageFromSDLSurface(Surface: PSDL_Surface; var ImageData: TImageData;
  63. OverrideFormat: TImageFormat = ifUnknown): Boolean;
  64. implementation
  65. const
  66. DefaultFlags = SDL_SWSURFACE;
  67. function Iff(Condition: Boolean; const TruePart, FalsePart: TImageFormat): TImageFormat; overload;
  68. begin
  69. if Condition then
  70. Result := TruePart
  71. else
  72. Result := FalsePart;
  73. end;
  74. function LoadSDLSurfaceFromFile(const FileName: string): PSDL_Surface;
  75. var
  76. ImageData: TImageData;
  77. begin
  78. InitImage(ImageData);
  79. if LoadImageFromFile(FileName, ImageData) then
  80. Result := CreateSDLSurfaceFromImage(ImageData, DefaultFlags)
  81. else
  82. Result := nil;
  83. FreeImage(ImageData);
  84. end;
  85. function LoadSDLSurfaceFromStream(Stream: TStream): PSDL_Surface;
  86. var
  87. ImageData: TImageData;
  88. begin
  89. InitImage(ImageData);
  90. if LoadImageFromStream(Stream, ImageData) then
  91. Result := CreateSDLSurfaceFromImage(ImageData, DefaultFlags)
  92. else
  93. Result := nil;
  94. FreeImage(ImageData);
  95. end;
  96. function LoadSDLSurfaceFromMemory(Data: Pointer; Size: LongInt): PSDL_Surface;
  97. var
  98. ImageData: TImageData;
  99. begin
  100. InitImage(ImageData);
  101. if LoadImageFromMemory(Data, Size, ImageData) then
  102. Result := CreateSDLSurfaceFromImage(ImageData, DefaultFlags)
  103. else
  104. Result := nil;
  105. FreeImage(ImageData);
  106. end;
  107. function CreateSDLSurfaceFromImage(const ImageData: TImageData;
  108. Flags: LongWord; OverrideFormat: TImageFormat): PSDL_Surface;
  109. var
  110. WorkData: TImageData;
  111. Info: TImageFormatInfo;
  112. ConvFormat: TImageFormat;
  113. AMask, RMask, GMask, BMask: LongWord;
  114. I, LineBytes: LongInt;
  115. procedure DetermineSDLMasks(var AMask, RMask, GMask, BMask: LongWord);
  116. begin
  117. if Info.UsePixelFormat then
  118. begin
  119. AMask := Info.PixelFormat.ABitMask;
  120. RMask := Info.PixelFormat.RBitMask;
  121. GMask := Info.PixelFormat.GBitMask;
  122. BMask := Info.PixelFormat.BBitMask;
  123. end
  124. else
  125. begin
  126. AMask := IffUnsigned(Info.HasAlphaChannel, $FF000000, 0);
  127. RMask := $00FF0000;
  128. GMask := $0000FF00;
  129. BMask := $000000FF;
  130. end;
  131. end;
  132. begin
  133. Result := nil;
  134. if TestImage(ImageData) then
  135. begin
  136. InitImage(WorkData);
  137. CloneImage(ImageData, WorkData);
  138. // Image is converted to override format
  139. if OverrideFormat <> ifUnknown then
  140. ConvertImage(WorkData, OverrideFormat);
  141. GetImageFormatInfo(WorkData.Format, Info);
  142. // Image is first converted to format supported by SDL
  143. if Info.IsFloatingPoint or Info.IsSpecial then
  144. ConvFormat := ifA8R8G8B8
  145. else
  146. if Info.UsePixelFormat then
  147. begin
  148. if Info.BytesPerPixel < 2 then
  149. ConvFormat := Iff(Info.HasAlphaChannel, ifA4R4G4B4, ifR5G6B5)
  150. else
  151. ConvFormat := WorkData.Format;
  152. end
  153. else
  154. if Info.IsIndexed then
  155. ConvFormat := ifIndex8
  156. else
  157. ConvFormat := Iff(Info.HasAlphaChannel, ifA8R8G8B8, ifR8G8B8);
  158. ConvertImage(WorkData, ConvFormat);
  159. GetImageFormatInfo(WorkData.Format, Info);
  160. // Channel masks are determined based on image's format,
  161. // only 8/16/24/32bit images should be here now
  162. DetermineSDLMasks(AMask, RMask, GMask, BMask);
  163. // SDL surface is created
  164. Result := SDL_CreateRGBSurface(Flags, WorkData.Width, WorkData.Height,
  165. Info.BytesPerPixel * 8, RMask, GMask, BMask, AMask);
  166. if Result <> nil then
  167. begin
  168. LineBytes := Info.BytesPerPixel * WorkData.Width;
  169. if SDL_MustLock(Result) then
  170. SDL_LockSurface(Result);
  171. // Pixels of image are copied to SDL surface
  172. if LineBytes = Result.pitch then
  173. Move(WorkData.Bits^, Result.pixels^, WorkData.Size)
  174. else
  175. for I := 0 to WorkData.Height - 1 do
  176. Move(PByteArray(WorkData.Bits)[I * LineBytes],
  177. PByteArray(Result.pixels)[I * Result.pitch], LineBytes);
  178. if SDL_MustLock(Result) then
  179. SDL_UnlockSurface(Result);
  180. // If surface is in indexed format, palette is copied
  181. if (Info.Format = ifIndex8) and (Result.format.palette <> nil) then
  182. begin
  183. Result.format.palette.ncolors := Info.PaletteEntries;
  184. for I := 0 to Info.PaletteEntries - 1 do
  185. begin
  186. Result.format.palette.colors[I].r := WorkData.Palette[I].R;
  187. Result.format.palette.colors[I].g := WorkData.Palette[I].G;
  188. Result.format.palette.colors[I].b := WorkData.Palette[I].B;
  189. Result.format.palette.colors[I].unused := 0;
  190. end;
  191. end;
  192. end;
  193. FreeImage(WorkData);
  194. end;
  195. end;
  196. function SaveSDLSurfaceToFile(const FileName: string; Surface: PSDL_Surface): Boolean;
  197. var
  198. ImageData: TImageData;
  199. begin
  200. Result := False;
  201. if CreateImageFromSDLSurface(Surface, ImageData) then
  202. begin
  203. Result := SaveImageToFile(FileName, ImageData);
  204. FreeImage(ImageData);
  205. end;
  206. end;
  207. function SaveSDLSurfaceToStream(const Ext: string; Stream: TStream; Surface: PSDL_Surface): Boolean;
  208. var
  209. ImageData: TImageData;
  210. begin
  211. Result := False;
  212. if CreateImageFromSDLSurface(Surface, ImageData) then
  213. begin
  214. Result := SaveImageToStream(Ext, Stream, ImageData);
  215. FreeImage(ImageData);
  216. end;
  217. end;
  218. function SaveSDLSurfaceToMemory(const Ext: string; Data: Pointer; var Size: LongInt; Surface: PSDL_Surface): Boolean;
  219. var
  220. ImageData: TImageData;
  221. begin
  222. Result := False;
  223. if CreateImageFromSDLSurface(Surface, ImageData) then
  224. begin
  225. Result := SaveImageToMemory(Ext, Data, Size, ImageData);
  226. FreeImage(ImageData);
  227. end;
  228. end;
  229. function CreateImageFromSDLSurface(Surface: PSDL_Surface; var ImageData: TImageData;
  230. OverrideFormat: TImageFormat): Boolean;
  231. const
  232. SDL_A8R8G8B8Format: TSDL_PixelFormat = (palette: nil; BitsPerPixel: 32;
  233. BytesPerPixel: 4; Rloss: 0; Gloss: 0; Bloss: 0; Aloss: 0;
  234. Rshift: 16; Gshift: 8; Bshift: 0; Ashift: 24;
  235. Rmask: $00FF0000; Gmask: $0000FF00; Bmask: $000000FF; Amask: $FF000000;
  236. colorkey: 0; alpha: $FF);
  237. var
  238. Format: TImageFormat;
  239. Converted: PSDL_Surface;
  240. Info: TImageFormatInfo;
  241. I, LineBytes: LongInt;
  242. function DetermineImageFormat: TImageFormat;
  243. var
  244. Fmt: TImageFormat;
  245. begin
  246. Result := ifUnknown;
  247. case Surface.format.BitsPerPixel of
  248. 8: Result := ifIndex8;
  249. 16:
  250. begin
  251. // go trough 16bit formats supported by Imaging and
  252. // if there is one that matches SDL format's masks then use it
  253. for Fmt := ifR5G6B5 to ifX4R4G4B4 do
  254. begin
  255. GetImageFormatInfo(Fmt, Info);
  256. if (Info.PixelFormat.ABitMask = Surface.format.AMask) and
  257. (Info.PixelFormat.RBitMask = Surface.format.RMask) and
  258. (Info.PixelFormat.GBitMask = Surface.format.GMask) and
  259. (Info.PixelFormat.BBitMask = Surface.format.BMask) then
  260. begin
  261. Result := Fmt;
  262. Break;
  263. end;
  264. end;
  265. end;
  266. 24:
  267. begin
  268. if (Surface.format.RMask = $FF0000) and
  269. (Surface.format.GMask = $00FF00) and
  270. (Surface.format.BMask = $0000FF) then
  271. Result := ifR8G8B8;
  272. end;
  273. 32:
  274. begin
  275. if (Surface.format.RMask = $00FF0000) and
  276. (Surface.format.GMask = $0000FF00) and
  277. (Surface.format.BMask = $000000FF) then
  278. if (Surface.format.AMask = $FF000000) then
  279. Result := ifA8R8G8B8
  280. else
  281. Result := ifX8R8G8B8
  282. end;
  283. end;
  284. end;
  285. begin
  286. Result := False;
  287. FreeImage(ImageData);
  288. // See if surface is in format supported by Imaging and if it is
  289. // not then it is converted to A8R8G8B8
  290. Format := DetermineImageFormat;
  291. if Format = ifUnknown then
  292. begin
  293. Converted := SDL_ConvertSurface(Surface, @SDL_A8R8G8B8Format, SDL_SWSURFACE);
  294. Format := ifA8R8G8B8;
  295. end
  296. else
  297. Converted := Surface;
  298. if (Converted <> nil) and NewImage(Converted.w, Converted.h, Format, ImageData) then
  299. begin
  300. GetImageFormatInfo(Format, Info);
  301. LineBytes := Info.BytesPerPixel * ImageData.Width;
  302. if SDL_MustLock(Converted) then
  303. SDL_LockSurface(Converted);
  304. // New image is created and pixels are copied from SDL surface
  305. if LineBytes = Converted.pitch then
  306. Move(Converted.pixels^, ImageData.Bits^, ImageData.Size)
  307. else
  308. for I := 0 to ImageData.Height - 1 do
  309. Move(PByteArray(Converted.pixels)[I * Converted.pitch],
  310. PByteArray(ImageData.Bits)[I * LineBytes], LineBytes);
  311. if SDL_MustLock(Converted) then
  312. SDL_UnlockSurface(Converted);
  313. // Copy palette if necessary
  314. // If surface is in indexed format, palette is copied
  315. if (Info.Format = ifIndex8) and (Converted.format.palette <> nil) then
  316. begin
  317. for I := 0 to Min(Info.PaletteEntries, Converted.format.palette.ncolors) - 1 do
  318. begin
  319. ImageData.Palette[I].A := 255;
  320. ImageData.Palette[I].R := Converted.format.palette.colors[I].r;
  321. ImageData.Palette[I].G := Converted.format.palette.colors[I].g;
  322. ImageData.Palette[I].B := Converted.format.palette.colors[I].b;
  323. end;
  324. end;
  325. // Image is converted to override format
  326. if OverrideFormat <> ifUnknown then
  327. ConvertImage(ImageData, OverrideFormat);
  328. Result := True;
  329. end;
  330. if Converted <> Surface then
  331. SDL_FreeSurface(Converted);
  332. end;
  333. {
  334. File Notes:
  335. -- TODOS ----------------------------------------------------
  336. - nothing now
  337. -- 0.23 Changes/Bug Fixes -----------------------------------
  338. - Fixed possible int overflow in CreateSDLSurfaceFromImage.
  339. -- 0.15 Changes/Bug Fixes -----------------------------------
  340. - unit created and initial stuff added
  341. }
  342. end.