ImagingSdl.pas 12 KB

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