uimagebackup.pas 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471
  1. // SPDX-License-Identifier: GPL-3.0-only
  2. unit UImageBackup;
  3. {$mode objfpc}{$H+}
  4. interface
  5. uses
  6. Classes, SysUtils, BGRABitmap, BGRABitmapTypes;
  7. type
  8. TRowBackupArray = packed array of packed record
  9. Left, Right: Word;
  10. DataLen: integer;
  11. DataPos: Int64;
  12. end;
  13. { TImageBackup }
  14. TImageBackup = class
  15. private
  16. FRows: TRowBackupArray;
  17. FWidth,FHeight: integer;
  18. FBounds: TRect;
  19. FGrayscale: boolean;
  20. FData: TMemoryStream;
  21. procedure Init(ASource, ASource2: TBGRABitmap; AGrayscale: boolean; ASourceBounds: TRect);
  22. public
  23. constructor Create;
  24. constructor Create(ASource: TBGRABitmap; AGrayscale: boolean);
  25. constructor Create(ASource: TBGRABitmap; AGrayscale: boolean; ABounds: TRect);
  26. constructor Create(ASource, ASource2: TBGRABitmap; AGrayscale: boolean; ABounds: TRect);
  27. procedure Restore(ADest: TBGRABitmap; ARect: TRect; AXor: boolean);
  28. procedure SaveToStream(ADest: TStream);
  29. procedure LoadFromStream(ASource: TStream);
  30. destructor Destroy; override;
  31. function UsedMemory: int64;
  32. property Width: integer read FWidth;
  33. property Height: integer read FHeight;
  34. property Grayscale: boolean read FGrayscale;
  35. property Bounds: TRect read FBounds;
  36. end;
  37. implementation
  38. uses BGRALzpCommon, math;
  39. { TImageBackup }
  40. procedure TImageBackup.Init(ASource, ASource2: TBGRABitmap; AGrayscale: boolean;
  41. ASourceBounds: TRect);
  42. var
  43. tempRow: Pointer;
  44. pPix, pPix2: PBGRAPixel;
  45. sourceWidth, sourceWidth2: integer;
  46. procedure CopyChannelGrayscale(y, ARowWidth: integer);
  47. var
  48. pSrc, pTempRow: PByte;
  49. remain: LongInt;
  50. begin
  51. if Assigned(ASource) then
  52. begin
  53. pTempRow := PByte(tempRow);
  54. pSrc := ASource.GetPixelAddress(FRows[y].Left, y) + TBGRAPixel_GreenByteOffset;
  55. remain := ARowWidth;
  56. while remain > 0 do
  57. begin
  58. pTempRow^ := pSrc^;
  59. inc(pSrc, 4);
  60. inc(pTempRow);
  61. dec(remain);
  62. end;
  63. end else fillchar(tempRow^, ARowWidth, 0);
  64. if Assigned(ASource2) then
  65. begin
  66. pTempRow := PByte(tempRow);
  67. pSrc := ASource2.GetPixelAddress(FRows[y].Left, y) + TBGRAPixel_GreenByteOffset;
  68. remain := ARowWidth;
  69. while remain > 0 do
  70. begin
  71. pTempRow^ := pTempRow^ xor pSrc^;
  72. inc(pSrc, 4);
  73. inc(pTempRow);
  74. dec(remain);
  75. end;
  76. end;
  77. end;
  78. procedure CopyChannelsRGBA(y, ARowWidth: integer);
  79. var
  80. pSrc, pTempRow, pTempRow2, pTempRow3, pTempRow4: PByte;
  81. remain: LongInt;
  82. begin
  83. if Assigned(ASource) then
  84. begin
  85. pTempRow := PByte(tempRow);
  86. pTempRow2 := pTempRow + ARowWidth;
  87. pTempRow3 := pTempRow + (ARowWidth shl 1);
  88. pTempRow4 := pTempRow + ARowWidth*3;
  89. pSrc := ASource.GetPixelAddress(FRows[y].Left, y);
  90. remain := ARowWidth;
  91. while remain > 0 do
  92. begin
  93. pTempRow^ := pSrc^;
  94. pTempRow2^ := (pSrc+1)^;
  95. pTempRow3^ := (pSrc+2)^;
  96. pTempRow4^ := (pSrc+3)^;
  97. inc(pSrc, 4);
  98. inc(pTempRow);
  99. inc(pTempRow2);
  100. inc(pTempRow3);
  101. inc(pTempRow4);
  102. dec(remain);
  103. end;
  104. end else fillchar(tempRow^, ARowWidth*4, 0);
  105. if Assigned(ASource2) then
  106. begin
  107. pTempRow := PByte(tempRow);
  108. pTempRow2 := pTempRow + ARowWidth;
  109. pTempRow3 := pTempRow + (ARowWidth shl 1);
  110. pTempRow4 := pTempRow + ARowWidth*3;
  111. pSrc := ASource2.GetPixelAddress(FRows[y].Left, y);
  112. remain := ARowWidth;
  113. while remain > 0 do
  114. begin
  115. pTempRow^ := pTempRow^ xor pSrc^;
  116. pTempRow2^ := pTempRow2^ xor (pSrc+1)^;
  117. pTempRow3^ := pTempRow3^ xor (pSrc+2)^;
  118. pTempRow4^ := pTempRow4^ xor (pSrc+3)^;
  119. inc(pSrc, 4);
  120. inc(pTempRow);
  121. inc(pTempRow2);
  122. inc(pTempRow3);
  123. inc(pTempRow4);
  124. dec(remain);
  125. end;
  126. end;
  127. end;
  128. procedure EncodeChannels(y: integer);
  129. var
  130. rowWidth: Integer;
  131. begin
  132. rowWidth := FRows[y].Right - FRows[y].Left;
  133. if FGrayscale then
  134. begin
  135. CopyChannelGrayscale(y, rowWidth);
  136. EncodeLazRLE(tempRow^, rowWidth, FData);
  137. end
  138. else
  139. begin
  140. CopyChannelsRGBA(y, rowWidth);
  141. EncodeLazRLE(tempRow^, rowWidth*4, FData);
  142. end;
  143. end;
  144. function CheckPixel(x: integer): boolean; inline;
  145. var value: DWord;
  146. begin
  147. if FGrayscale then
  148. begin
  149. value := 0;
  150. if Assigned(pPix) and (x < sourceWidth) then value := (pPix+x)^.green;
  151. if Assigned(pPix2) and (x < SourceWidth2) then value := value xor (pPix2+x)^.green;
  152. end else
  153. begin
  154. value := 0;
  155. if Assigned(pPix) and (x < sourceWidth) then value := PDWord(pPix+x)^;
  156. if Assigned(pPix2) and (x < sourceWidth2) then value := value xor PDWord(pPix2+x)^;
  157. end;
  158. result := value <> 0;
  159. end;
  160. procedure IncludeBound(y: integer);
  161. begin
  162. if y < FBounds.Top then FBounds.Top := y;
  163. if y+1 > FBounds.Bottom then FBounds.Bottom := y+1;
  164. if FRows[y].Left < FBounds.Left then FBounds.Left := FRows[y].Left;
  165. if FRows[y].Right > FBounds.Right then FBounds.Right := FRows[y].Right;
  166. end;
  167. var
  168. y,x,x2: LongInt;
  169. begin
  170. FWidth := 0;
  171. FHeight := 0;
  172. if Assigned(ASource) then
  173. begin
  174. ASourceBounds.Intersect(rect(0,0,ASource.Width,ASource.Height));
  175. FWidth := ASource.Width;
  176. FHeight := ASource.Height;
  177. sourceWidth := ASource.Width;
  178. end else sourceWidth := 0;
  179. if Assigned(ASource2) then
  180. begin
  181. ASourceBounds.Intersect(rect(0,0,ASource2.Width,ASource2.Height));
  182. FWidth := max(FWidth, ASource2.Width);
  183. FHeight := max(FHeight, ASource2.Height);
  184. sourceWidth2 := ASource2.Width;
  185. end else sourceWidth2 := 0;
  186. FGrayscale := AGrayscale;
  187. FData := TMemoryStream.Create;
  188. FBounds := Rect(maxLongint, maxLongint, -maxLongint, -maxLongint);
  189. setlength(FRows, FHeight);
  190. if AGrayscale then
  191. Getmem(tempRow, ASourceBounds.Width)
  192. else Getmem(tempRow, ASourceBounds.Width*4);
  193. for y := ASourceBounds.Top to ASourceBounds.Bottom-1 do
  194. begin
  195. if Assigned(ASource) then pPix := ASource.ScanLine[y] else pPix := nil;
  196. if Assigned(ASource2) then pPix2 := ASource2.ScanLine[y] else pPix2 := nil;
  197. for x := ASourceBounds.Left to ASourceBounds.Right-1 do
  198. begin
  199. if CheckPixel(x) then //row start found
  200. begin
  201. FRows[y].Left:= x;
  202. FRows[y].Right:= x;
  203. FRows[y].DataPos:= FData.Position;
  204. for x2 := ASourceBounds.Right-1 downto x do
  205. begin
  206. if CheckPixel(x2) then //row end found
  207. begin
  208. FRows[y].Right := x2+1;
  209. IncludeBound(y);
  210. EncodeChannels(y);
  211. FRows[y].DataLen:= FData.Position-FRows[y].DataPos;
  212. break;
  213. end;
  214. end;
  215. break;
  216. end;
  217. end;
  218. end;
  219. FreeMem(tempRow);
  220. if (FBounds.Right < FBounds.Left) or (FBounds.Bottom < FBounds.Top) then
  221. FBounds := EmptyRect;
  222. end;
  223. constructor TImageBackup.Create;
  224. begin
  225. FWidth:= 0;
  226. FHeight:= 0;
  227. FGrayscale:= false;
  228. FBounds:= EmptyRect;
  229. FData := nil;
  230. end;
  231. constructor TImageBackup.Create(ASource: TBGRABitmap; AGrayscale: boolean);
  232. begin
  233. Init(ASource, nil, AGrayscale, rect(0, 0, ASource.Width, ASource.Height));
  234. end;
  235. constructor TImageBackup.Create(ASource: TBGRABitmap; AGrayscale: boolean;
  236. ABounds: TRect);
  237. begin
  238. Init(ASource, nil, AGrayscale, ABounds);
  239. end;
  240. constructor TImageBackup.Create(ASource, ASource2: TBGRABitmap;
  241. AGrayscale: boolean; ABounds: TRect);
  242. begin
  243. Init(ASource, ASource2, AGrayscale, ABounds);
  244. end;
  245. procedure TImageBackup.Restore(ADest: TBGRABitmap; ARect: TRect; AXor: boolean);
  246. var
  247. tempRow: Pointer;
  248. procedure RestoreRow(y: integer; AInnerStart, AInnerEnd: integer);
  249. var
  250. rowWidth: integer;
  251. pData: PByte;
  252. remain: integer;
  253. procedure DecodeChannels;
  254. var
  255. dataWidth: integer;
  256. decoded: PtrInt;
  257. begin
  258. FData.Position := FRows[y].DataPos;
  259. if not FGrayscale then dataWidth := rowWidth*4 else dataWidth := rowWidth;
  260. decoded := DecodeLazRLE(FData, tempRow^, dataWidth, FRows[y].DataLen);
  261. if decoded < dataWidth then fillChar((PByte(tempRow)+decoded)^, 0, dataWidth - decoded);
  262. pData := PByte(tempRow) + AInnerStart;
  263. remain := AInnerEnd - AInnerStart;
  264. end;
  265. var
  266. pDest: PBGRAPixel;
  267. rowWidth3: Integer;
  268. begin
  269. if AInnerEnd <= AInnerStart then exit;
  270. rowWidth := FRows[y].Right - FRows[y].Left;
  271. rowWidth3 := rowWidth*3;
  272. DecodeChannels;
  273. pDest := PBGRAPixel(ADest.GetPixelAddress(FRows[y].Left + AInnerStart, y));
  274. if FGrayscale then
  275. begin
  276. if AXor then
  277. begin
  278. while remain > 0 do
  279. begin
  280. pDest^.green := pDest^.green xor pData^;
  281. pDest^.red := pDest^.green;
  282. pDest^.blue := pDest^.green;
  283. pDest^.alpha := 255;
  284. inc(pData);
  285. inc(pDest);
  286. dec(remain);
  287. end;
  288. end else
  289. begin
  290. while remain > 0 do
  291. begin
  292. pDest^.red := pData^;
  293. pDest^.green := pData^;
  294. pDest^.blue := pData^;
  295. pDest^.alpha := 255;
  296. inc(pData);
  297. inc(pDest);
  298. dec(remain);
  299. end;
  300. end;
  301. end else
  302. begin
  303. if AXor then
  304. begin
  305. while remain > 0 do
  306. begin
  307. PByte(pDest)^ := PByte(pDest)^ xor pData^;
  308. (PByte(pDest)+1)^ := (PByte(pDest)+1)^ xor (pData+rowWidth)^;
  309. (PByte(pDest)+2)^ := (PByte(pDest)+2)^ xor (pData+(rowWidth shl 1))^;
  310. (PByte(pDest)+3)^ := (PByte(pDest)+3)^ xor (pData+rowWidth3)^;
  311. inc(pData);
  312. inc(pDest);
  313. dec(remain);
  314. end;
  315. end else
  316. begin
  317. while remain > 0 do
  318. begin
  319. PByte(pDest)^ := pData^;
  320. (PByte(pDest)+1)^ := (pData+rowWidth)^;
  321. (PByte(pDest)+2)^ := (pData+(rowWidth shl 1))^;
  322. (PByte(pDest)+3)^ := (pData+rowWidth3)^;
  323. inc(pData);
  324. inc(pDest);
  325. dec(remain);
  326. end;
  327. end;
  328. end;
  329. end;
  330. var
  331. emptyColor: TBGRAPixel;
  332. procedure SetEmpty(x, y, x2: integer);
  333. begin
  334. if not AXor then
  335. ADest.SetHorizLine(x, y, x2, emptyColor);
  336. end;
  337. var rowInnerStart, y: integer;
  338. begin
  339. ARect.Intersect(Bounds);
  340. ARect.Intersect(ADest.ClipRect);
  341. if ARect.IsEmpty then exit;
  342. if FGrayscale then
  343. begin
  344. GetMem(tempRow, FWidth);
  345. emptyColor := BGRABlack;
  346. end else
  347. begin
  348. GetMem(tempRow, FWidth*4);
  349. emptyColor := BGRAPixelTransparent;
  350. end;
  351. for y := ARect.Top to ARect.Bottom-1 do
  352. begin
  353. if FRows[y].Left >= ARect.Left then
  354. begin
  355. if FRows[y].Left >= ARect.Right then
  356. begin
  357. SetEmpty(ARect.Left, y, ARect.Right-1);
  358. Continue;
  359. end
  360. else
  361. begin
  362. if FRows[y].Left > ARect.Left then
  363. SetEmpty(ARect.Left, y, FRows[y].Left-1);
  364. rowInnerStart := 0;
  365. end;
  366. end else
  367. if FRows[y].Right <= ARect.Left then Continue else
  368. rowInnerStart := ARect.Left - FRows[y].Left;
  369. if FRows[y].Right >= ARect.Right then
  370. RestoreRow(y, rowInnerStart, ARect.Right - FRows[y].Left) else
  371. begin
  372. RestoreRow(y, rowInnerStart, FRows[y].Right - FRows[y].Left);
  373. SetEmpty(FRows[y].Right, y, ARect.Right-1);
  374. end;
  375. end;
  376. FreeMem(tempRow);
  377. end;
  378. procedure TImageBackup.SaveToStream(ADest: TStream);
  379. var dataSize: int64;
  380. lenRows: Integer;
  381. begin
  382. ADest.WriteBuffer(FWidth, sizeof(FWidth));
  383. ADest.WriteBuffer(FHeight, sizeof(FHeight));
  384. ADest.WriteBuffer(FBounds, sizeof(FBounds));
  385. ADest.WriteBuffer(FGrayscale, sizeof(FGrayscale));
  386. lenRows := length(FRows);
  387. ADest.WriteBuffer(lenRows, sizeof(lenRows));
  388. if lenRows > 0 then ADest.WriteBuffer(FRows[0], sizeof(FRows[0])*lenRows);
  389. dataSize := FData.Size;
  390. ADest.WriteBuffer(dataSize, sizeof(dataSize));
  391. FData.Position:= 0;
  392. ADest.CopyFrom(FData, dataSize);
  393. end;
  394. procedure TImageBackup.LoadFromStream(ASource: TStream);
  395. var newWidth, newHeight: integer;
  396. newGrayscale: boolean;
  397. newBounds: TRect;
  398. newRows: TRowBackupArray;
  399. dataSize: int64;
  400. lenRows: Integer;
  401. begin
  402. ASource.ReadBuffer({%H-}newWidth, sizeof(newWidth));
  403. ASource.ReadBuffer({%H-}newHeight, sizeof(newHeight));
  404. ASource.ReadBuffer({%H-}newBounds, sizeof(newBounds));
  405. ASource.ReadBuffer({%H-}newGrayscale, sizeof(newGrayscale));
  406. ASource.ReadBuffer({%H-}lenRows, sizeof(lenRows));
  407. newRows := nil;
  408. setlength(newRows, lenRows);
  409. if lenRows > 0 then ASource.ReadBuffer(newRows[0], sizeof(newRows[0])*length(newRows));
  410. ASource.ReadBuffer({%H-}dataSize, sizeof(dataSize));
  411. FreeAndNil(FData);
  412. FData := TMemoryStream.Create;
  413. FData.CopyFrom(ASource, dataSize);
  414. FWidth := newWidth;
  415. FHeight := newHeight;
  416. FBounds := newBounds;
  417. FGrayscale:= newGrayscale;
  418. FRows := newRows;
  419. end;
  420. destructor TImageBackup.Destroy;
  421. begin
  422. FData.Free;
  423. inherited Destroy;
  424. end;
  425. function TImageBackup.UsedMemory: int64;
  426. begin
  427. result := 0;
  428. if Assigned(FData) then result := FData.Size;
  429. if FRows <> nil then
  430. inc(result, length(FRows)*sizeof(FRows[0]));
  431. end;
  432. end.