Resample.pas 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211
  1. unit Resample;
  2. interface
  3. uses
  4. Windows, Math, Graphics;
  5. function StretchBmp(SrcBitmap, DstBitmap: TBitmap;
  6. DstWidth, DstHeight: Integer; Is32bit: Boolean): Boolean;
  7. implementation
  8. const
  9. FixedBits = 16;
  10. FixedOne = 1 shl FixedBits;
  11. FixedOneHalf = FixedOne shr 1;
  12. type
  13. TWeight = packed record
  14. Offset: Integer; //Byte offset to pixel data
  15. case Integer of
  16. 0: (Weight: Integer); //Pixel weight in Q16.16 fixed point format
  17. 1: (Temp: Single); //same thing in float format
  18. end;
  19. TWeightArray = array [0..MaxInt div SizeOf(TWeight) - 1] of TWeight;
  20. TPutPixelProc = procedure(const Weights: array of TWeight; Bits, Pixel: Pointer);
  21. procedure ResampleBits(DstSize, SrcSize: Integer; SrcLine, DstLine: Pointer;
  22. PixelSize, LineCount, SrcLineSize, DstLineSize: Integer; PutPixelProc: TPutPixelProc);
  23. var
  24. I, J, Count: Integer;
  25. Limit, Scale, X, Y, Center, Sup, Sum: Single;
  26. Weights: ^TWeightArray;
  27. Src, Dst: Pointer;
  28. const
  29. FilterWidth = 2.0;
  30. begin
  31. Scale := SrcSize / DstSize;
  32. if Scale < 1.0 then
  33. Limit := 1.0
  34. else
  35. Limit := 1.0 / Scale;
  36. Sup := FilterWidth / Limit;
  37. GetMem(Weights, Trunc(Sup * 2.0 + 2.0) * SizeOf(TWeight));
  38. try
  39. for I := 0 to DstSize - 1 do begin
  40. Count := 0;
  41. Sum := 0;
  42. Center := (I + 0.5) * Scale;
  43. for J := Floor(Center - Sup) to Ceil(Center + Sup) do begin
  44. X := Abs(J - Center + 0.5);
  45. if X > Sup then Continue;
  46. X := X * Limit;
  47. {Resampling filter}
  48. if X < 1.0 then //SPLINE16
  49. Y := Sqr(X) * (X - 9 / 5) - 1 / 5 * X + 1
  50. else
  51. Y := Sqr(X - 1) * (-1 / 3 * (X - 1) + 4 / 5) - 7 / 15 * (X - 1);
  52. {The code from above must be kept in sync with FilterWidth value}
  53. if (Y = 0) or (J < 0) or (J >= SrcSize) then Continue;
  54. with Weights[Count] do begin
  55. Temp := Y;
  56. Offset := J * PixelSize;
  57. end;
  58. Sum := Sum + Y;
  59. Inc(Count);
  60. end;
  61. if Sum <> 0 then begin
  62. Sum := FixedOne / Sum;
  63. for J := 0 to Count - 1 do
  64. with Weights[J] do
  65. Weight := Round(Temp * Sum);
  66. end else
  67. Count := 0;
  68. Src := SrcLine;
  69. Dst := DstLine;
  70. for J := 0 to LineCount - 1 do begin
  71. PutPixelProc(Slice(Weights^, Count), Src, Dst);
  72. Inc(PByte(Src), SrcLineSize);
  73. Inc(PByte(Dst), DstLineSize);
  74. end;
  75. Inc(PByte(DstLine), PixelSize);
  76. end;
  77. finally
  78. FreeMem(Weights);
  79. end;
  80. end;
  81. //Process pixel in BGR format
  82. procedure PutPixel24(const Weights: array of TWeight; Bits, Pixel: Pointer);
  83. type
  84. PRGBTriple = ^TRGBTriple;
  85. var
  86. I, R, G, B: Integer;
  87. begin
  88. R := FixedOneHalf;
  89. G := FixedOneHalf;
  90. B := FixedOneHalf;
  91. for I := 0 to High(Weights) do
  92. with Weights[I], PRGBTriple(PAnsiChar(Bits) + Offset)^ do begin
  93. Inc(R, rgbtRed * Weight);
  94. Inc(G, rgbtGreen * Weight);
  95. Inc(B, rgbtBlue * Weight);
  96. end;
  97. with PRGBTriple(Pixel)^ do begin
  98. //Clamps all channels to values between 0 and 255
  99. if R > 0 then if R < 255 shl FixedBits then rgbtRed := R shr FixedBits else rgbtRed := 255 else rgbtRed := 0;
  100. if G > 0 then if G < 255 shl FixedBits then rgbtGreen := G shr FixedBits else rgbtGreen := 255 else rgbtGreen := 0;
  101. if B > 0 then if B < 255 shl FixedBits then rgbtBlue := B shr FixedBits else rgbtBlue := 255 else rgbtBlue := 0;
  102. end;
  103. end;
  104. //Process pixel in BGRA premultiplied alpha format
  105. procedure PutPixel32P(const Weights: array of TWeight; Bits, Pixel: Pointer);
  106. var
  107. I, R, G, B, A: Integer;
  108. AByte: Byte;
  109. begin
  110. R := FixedOneHalf;
  111. G := FixedOneHalf;
  112. B := FixedOneHalf;
  113. A := FixedOneHalf;
  114. for I := 0 to High(Weights) do
  115. with Weights[I], PRGBQuad(PAnsiChar(Bits) + Offset)^ do begin
  116. Inc(R, rgbRed * Weight);
  117. Inc(G, rgbGreen * Weight);
  118. Inc(B, rgbBlue * Weight);
  119. Inc(A, rgbReserved * Weight);
  120. end;
  121. //Clamps alpha channel to values between 0 and 255
  122. if A > 0 then if A < 255 shl FixedBits then AByte := A shr FixedBits else AByte := 255 else AByte := 0;
  123. with PRGBQuad(Pixel)^ do begin
  124. rgbReserved := AByte;
  125. I := AByte shl FixedBits;
  126. //Clamps other channels to values between 0 and Alpha
  127. if R > 0 then if R < I then rgbRed := R shr FixedBits else rgbRed := AByte else rgbRed := 0;
  128. if G > 0 then if G < I then rgbGreen := G shr FixedBits else rgbGreen := AByte else rgbGreen := 0;
  129. if B > 0 then if B < I then rgbBlue := B shr FixedBits else rgbBlue := AByte else rgbBlue := 0;
  130. end;
  131. end;
  132. function StretchBmp(SrcBitmap, DstBitmap: TBitmap;
  133. DstWidth, DstHeight: Integer; Is32bit: Boolean): Boolean;
  134. var
  135. SrcWidth, SrcHeight, SrcLineSize, DstLineSize, PixelSize: Integer;
  136. SrcBits, DstBits, TmpBits: Pointer;
  137. PixelFormat: TPixelFormat;
  138. Proc: TPutPixelProc;
  139. begin
  140. Result := False;
  141. try
  142. if (DstWidth <= 0) or (DstHeight <= 0) then Exit;
  143. SrcWidth := SrcBitmap.Width;
  144. SrcHeight := SrcBitmap.Height;
  145. if (SrcWidth <= 0) or (SrcHeight <= 0) then Exit;
  146. if Is32bit then begin
  147. PixelFormat := pf32bit;
  148. PixelSize := 4;
  149. Proc := PutPixel32P;
  150. end else begin
  151. PixelFormat := pf24bit;
  152. PixelSize := 3;
  153. Proc := PutPixel24;
  154. end;
  155. //NOTE: Irreversible change of SrcBitmap pixel format
  156. SrcBitmap.PixelFormat := PixelFormat;
  157. SrcLineSize := WPARAM(SrcBitmap.ScanLine[0]) - WPARAM(SrcBitmap.ScanLine[1]);
  158. if SrcLineSize >= 0 then
  159. SrcBits := SrcBitmap.ScanLine[SrcHeight - 1]
  160. else begin
  161. SrcLineSize := -SrcLineSize;
  162. SrcBits := SrcBitmap.ScanLine[0];
  163. end;
  164. DstBitmap.PixelFormat := PixelFormat;
  165. DstBitmap.AlphaFormat := SrcBitmap.AlphaFormat;
  166. DstBitmap.Width := DstWidth;
  167. DstBitmap.Height := DstHeight;
  168. DstLineSize := WPARAM(DstBitmap.ScanLine[0]) - WPARAM(DstBitmap.ScanLine[1]);
  169. if DstLineSize >= 0 then
  170. DstBits := DstBitmap.ScanLine[DstHeight - 1]
  171. else begin
  172. DstLineSize := -DstLineSize;
  173. DstBits := DstBitmap.ScanLine[0];
  174. end;
  175. TmpBits := nil;
  176. try
  177. //Minimize temporary allocations by choosing right stretch order
  178. if DstWidth * SrcHeight < DstHeight * SrcWidth then begin
  179. GetMem(TmpBits, SrcHeight * DstLineSize);
  180. //Stretch horizontally
  181. ResampleBits(DstWidth, SrcWidth, SrcBits, TmpBits, PixelSize,
  182. SrcHeight, SrcLineSize, DstLineSize, Proc);
  183. //Stretch vertically
  184. ResampleBits(DstHeight, SrcHeight, TmpBits, DstBits, DstLineSize,
  185. DstWidth, PixelSize, PixelSize, Proc);
  186. end else begin
  187. GetMem(TmpBits, DstHeight * SrcLineSize);
  188. //Stretch vertically
  189. ResampleBits(DstHeight, SrcHeight, SrcBits, TmpBits, SrcLineSize,
  190. SrcWidth, PixelSize, PixelSize, Proc);
  191. //Stretch horizontally
  192. ResampleBits(DstWidth, SrcWidth, TmpBits, DstBits, PixelSize,
  193. DstHeight, SrcLineSize, DstLineSize, Proc);
  194. end;
  195. Result := True;
  196. finally
  197. FreeMem(TmpBits);
  198. end;
  199. except
  200. end;
  201. end;
  202. end.