Quellcode durchsuchen

Improved stretching by DRON + other tweaks.

Martijn Laan vor 7 Jahren
Ursprung
Commit
b02257385c
4 geänderte Dateien mit 231 neuen und 23 gelöschten Zeilen
  1. 20 17
      Components/BitmapImage.pas
  2. 198 0
      Components/Resample.pas
  3. 2 2
      ISHelp/isetup.xml
  4. 11 4
      whatsnew.htm

+ 20 - 17
Components/BitmapImage.pas

@@ -14,7 +14,7 @@ interface
 {$I ..\Projects\VERSION.INC}
 
 uses
-  Windows, Controls, Graphics, Classes;
+  Windows, Controls, Graphics, Classes, Resample;
 
 type
 {$IFNDEF IS_D3}
@@ -239,24 +239,27 @@ begin
     if Stretch then begin
       W := R.Right;
       H := R.Bottom;
-      if not Is32bit then begin
-        if not FStretchedBitmapValid or (FStretchedBitmap.Width <> W) or
-           (FStretchedBitmap.Height <> H) then begin
-          FStretchedBitmapValid := True;
-          if (FBitmap.Width = W) and (FBitmap.Height = H) then
-            FStretchedBitmap.Assign(FBitmap)
-          else begin
-            FStretchedBitmap.Assign(nil);
-            FStretchedBitmap.Palette := CopyPalette(FBitmap.Palette);
-            FStretchedBitmap.Width := W;
-            FStretchedBitmap.Height := H;
-            FStretchedBitmap.Canvas.StretchDraw(R, FBitmap);
+      Bmp := FStretchedBitmap;
+      if not FStretchedBitmapValid or (FStretchedBitmap.Width <> W) or
+         (FStretchedBitmap.Height <> H) then begin
+        FStretchedBitmapValid := True;
+        if (FBitmap.Width = W) and (FBitmap.Height = H) then
+          FStretchedBitmap.Assign(FBitmap)
+        else begin
+          FStretchedBitmap.Assign(nil);
+          if not StretchBmp(Canvas, FBitmap, FStretchedBitmap, W, H, Is32bit) then begin
+            if Is32bit then begin
+              FStretchedBitmapValid := False;
+              Bmp := FBitmap;
+            end else begin
+              FStretchedBitmap.Palette := CopyPalette(FBitmap.Palette);
+              FStretchedBitmap.Width := W;
+              FStretchedBitmap.Height := H;
+              FStretchedBitmap.Canvas.StretchDraw(R, FBitmap);
+            end;
           end;
         end;
-        Bmp := FStretchedBitmap;
-      end
-      else
-        Bmp := FBitmap;
+      end;
     end else begin
       Bmp := FBitmap;
       W := Bmp.Width;

+ 198 - 0
Components/Resample.pas

@@ -0,0 +1,198 @@
+unit Resample;
+interface
+uses
+  Windows, Math, Graphics;
+
+function StretchBmp(Canvas: TCanvas; SrcBitmap, DstBitmap: TBitmap;
+  DstWidth, DstHeight: Integer; Is32bit: Boolean): Boolean;
+
+implementation
+
+const
+  FixedBits = 16;
+type
+  TWeight = packed record
+    Offset: Integer;//Byte offset to pixel data
+    case Integer of
+      0: (Weight: Integer);//Pixel weight in Q16.16 fixed point format
+      1: (Temp: Single);//same thing in float format
+  end;
+  TWeightArray = array [0..MaxInt div SizeOf(TWeight) - 1] of TWeight;
+  TPutPixelProc = procedure(const Weights: array of TWeight; Bits: Pointer; var Pixel);
+
+procedure ResampleBits(DstSize, SrcSize: Integer; SrcLine, DstLine: Pointer;
+  PixelSize, LineCount, SrcLineSize, DstLineSize: Integer; PutPixelProc: TPutPixelProc);
+var
+  I, J, Count: Integer;
+  Limit, Scale, X, Y, Center, Sup, Sum: Single;
+  Weights: ^TWeightArray;
+  Src, Dst: Pointer;
+const
+  FilterWidth = 2.0;
+begin
+  Scale := SrcSize / DstSize;
+  if Scale < 1.0 then
+    Limit := 1.0
+  else
+    Limit := 1.0 / Scale;
+  Sup := FilterWidth / Limit;
+  GetMem(Weights, Trunc(Sup * 2.0 + 2.0) * SizeOf(TWeight));
+  try
+    for I := 0 to DstSize - 1 do begin
+      Count := 0;
+      Sum := 0;
+      Center := (I + 0.5) * Scale;
+      for J := Floor(Center - Sup) to Ceil(Center + Sup) do begin
+        X := Abs(J - Center + 0.5);
+        if X > Sup then Continue;
+        X := X * Limit;
+        {Resampling filter}
+        if X < 1.0 then //SPLINE16
+          Y := Sqr(X) * (X - 9 / 5) - 1 / 5 * X + 1
+        else
+          Y := Sqr(X - 1) * (-1 / 3 * (X - 1) + 4 / 5) - 7 / 15 * (X - 1);
+        {The code from above must be kept in sync with FilterWidth value}
+        if (Y = 0) or (J < 0) or (J >= SrcSize) then Continue;
+        with Weights[Count] do begin
+          Temp := Y;
+          Offset := J * PixelSize;
+        end;
+        Sum := Sum + Y;
+        Inc(Count);
+      end;
+      if Sum <> 0 then begin
+        Sum := (1 shl FixedBits) / Sum;
+        for J := 0 to Count - 1 do
+          with Weights[J] do
+            Weight := Round(Temp * Sum);
+      end else 
+        Count := 0;
+      Src := SrcLine;
+      Dst := DstLine;
+      for J := 0 to LineCount - 1 do begin
+        PutPixelProc(Slice(Weights^, Count), Src, Dst^);
+        Inc(PByte(Src), SrcLineSize);
+        Inc(PByte(Dst), DstLineSize);
+      end;
+      Inc(PByte(DstLine), PixelSize);
+    end;
+  finally
+    FreeMem(Weights);
+  end;
+end;
+
+//Process pixel in BGR format
+procedure PutPixel24(const Weights: array of TWeight; Bits: Pointer; var Pixel: TRGBTriple);
+type
+  PRGBTriple = ^TRGBTriple;
+var
+  I, R, G, B: Integer;
+begin
+  R := 0;
+  G := 0;
+  B := 0;
+  for I := 0 to High(Weights) do
+    with Weights[I], PRGBTriple(PAnsiChar(Bits) + Offset)^ do begin
+      Inc(R, rgbtRed * Weight);
+      Inc(G, rgbtGreen * Weight);
+      Inc(B, rgbtBlue * Weight);
+    end;
+  //Clamps all channels to values between 0 and 255  
+  if R < 0 then Pixel.rgbtRed   := 0 else if R > 255 shl FixedBits then Pixel.rgbtRed   := 255 else Pixel.rgbtRed   := R shr FixedBits;
+  if G < 0 then Pixel.rgbtGreen := 0 else if G > 255 shl FixedBits then Pixel.rgbtGreen := 255 else Pixel.rgbtGreen := G shr FixedBits;
+  if B < 0 then Pixel.rgbtBlue  := 0 else if B > 255 shl FixedBits then Pixel.rgbtBlue  := 255 else Pixel.rgbtBlue  := B shr FixedBits;
+end;
+
+//Process pixel in BGRA premultiplied alpha format
+procedure PutPixel32P(const Weights: array of TWeight; Bits: Pointer; var Pixel: TRGBQuad);
+var
+  I, R, G, B, A: Integer;
+begin
+  R := 0;
+  G := 0;
+  B := 0;
+  A := 0;
+  for I := 0 to High(Weights) do
+    with Weights[I], PRGBQuad(PAnsiChar(Bits) + Offset)^ do begin
+      Inc(R, rgbRed * Weight);
+      Inc(G, rgbGreen * Weight);
+      Inc(B, rgbBlue * Weight);
+      Inc(A, rgbReserved * Weight);
+    end;
+  //Clamps alpha channel to values between 0 and 255  
+  if A < 0 then Pixel.rgbReserved := 0 else if A > 255 shl FixedBits then Pixel.rgbReserved := 255 else Pixel.rgbReserved := A shr FixedBits;
+  A := Pixel.rgbReserved shl FixedBits;
+  //Clamps other channels to values between 0 and Alpha
+  if R < 0 then Pixel.rgbRed   := 0 else if R > A then Pixel.rgbRed   := A shr FixedBits else Pixel.rgbRed   := R shr FixedBits;
+  if G < 0 then Pixel.rgbGreen := 0 else if G > A then Pixel.rgbGreen := A shr FixedBits else Pixel.rgbGreen := G shr FixedBits;
+  if B < 0 then Pixel.rgbBlue  := 0 else if B > A then Pixel.rgbBlue  := A shr FixedBits else Pixel.rgbBlue  := B shr FixedBits;
+end;
+
+function StretchBmp(Canvas: TCanvas; SrcBitmap, DstBitmap: TBitmap;
+  DstWidth, DstHeight: Integer; Is32bit: Boolean): Boolean;
+var
+  SrcLineSize, DstLineSize, SrcWidth, SrcHeight, PixelSize: Integer;
+  SrcBits, DstBits, tmpBits: Pointer;
+  BI: TBitmapInfo;
+  DIB: HBITMAP;
+  Proc: TPutPixelProc;
+const
+  NULL = {$IFDEF VER90}nil{$ELSE}0{$ENDIF};
+begin
+  Result := False;
+  if (DstWidth <= 0) or (DstHeight <= 0) then Exit;
+  //High quality resampling makes sense only
+  //in True Color and High Color display modes.
+  if GetDeviceCaps(Canvas.Handle, BITSPIXEL) <= 8 then Exit;
+  SrcWidth  := SrcBitmap.Width;
+  SrcHeight := SrcBitmap.Height;
+  if (SrcWidth <= 0) or (SrcHeight <= 0) then Exit;
+  FillChar(BI, SizeOf(BI), 0);
+  BI.bmiHeader.biSize := SizeOf(BI.bmiHeader);
+  BI.bmiHeader.biWidth := SrcWidth;
+  BI.bmiHeader.biHeight := SrcHeight;
+  BI.bmiHeader.biPlanes := 1;
+  BI.bmiHeader.biCompression := BI_RGB;
+  if Is32bit then begin
+    BI.bmiHeader.biBitCount := 32;
+    PixelSize := 4;
+    Proc := @PutPixel32P;
+  end else begin
+    BI.bmiHeader.biBitCount := 24;
+    PixelSize := 3;
+    Proc := @PutPixel24;
+  end;
+  DstLineSize := (DstWidth * PixelSize + 3) and not 3;
+  SrcLineSize := (SrcWidth * PixelSize + 3) and not 3;
+  GetMem(tmpBits, SrcHeight * DstLineSize);
+  try
+    GetMem(SrcBits, SrcLineSize * SrcHeight);
+    try
+      if GetDIBits(Canvas.Handle, SrcBitmap.Handle,
+        0, SrcHeight, SrcBits, BI, DIB_RGB_COLORS) = 0 then Exit;
+      //Stretch horizontally  
+      ResampleBits(DstWidth, SrcWidth, SrcBits, tmpBits,
+        PixelSize, SrcHeight, SrcLineSize, DstLineSize, Proc);
+    finally
+      FreeMem(SrcBits);
+    end;
+    BI.bmiHeader.biWidth := DstWidth;
+    BI.bmiHeader.biHeight := DstHeight;
+    DIB := CreateDIBSection(Canvas.Handle, BI, DIB_RGB_COLORS, DstBits, NULL, 0);
+    if DIB = 0 then Exit;
+    try
+      //Stretch vertically  
+      ResampleBits(DstHeight, SrcHeight, tmpBits, DstBits,
+        DstLineSize, DstWidth, PixelSize, PixelSize, Proc);
+      DstBitmap.Handle := DIB;
+      Result := True;
+    except
+      DeleteObject(DIB);
+      raise;
+    end;
+  finally
+    FreeMem(tmpBits);
+  end;
+end;
+
+end.

+ 2 - 2
ISHelp/isetup.xml

@@ -4454,7 +4454,7 @@ DiskSliceSize=1457664
 <body>
 <p>Specifies the name(s) of the bitmap file(s) to display on the left side of the wizard. The files(s) must be located in your installation's <link topic="sourcedirectorynotes">source directory</link> when running the Setup Compiler, unless a fully qualified pathname is specified or the pathname is prefixed by "compiler:", in which case it looks for the file in the Compiler directory.</p>
 <p>256-color bitmaps may not display correctly in 256-color mode, since it does not handle palettes.</p>
-<p>When multiple files are specified, Setup will select the one which best matches the system's DPI setting. The recommended size of the bitmap per DPI setting is:</p>
+<p>When multiple files are specified, Setup will automatically select the one which best matches the system's DPI setting. The recommended size of the bitmap per DPI setting is:</p>
 <table>
 <tr><td>100%</td><td>164x314</td></tr>
 <tr><td>125%</td><td>192x386</td></tr>
@@ -4477,7 +4477,7 @@ DiskSliceSize=1457664
 <body>
 <p>Specifies the name(s) of the bitmap file(s) to display in the upper right corner of the wizard. The file(s) must be located in your installation's <link topic="sourcedirectorynotes">source directory</link> when running the Setup Compiler, unless a fully qualified pathname is specified or the pathname is prefixed by "compiler:", in which case it looks for the file in the Compiler directory.</p>
 <p>256-color bitmaps may not display correctly in 256-color mode, since it does not handle palettes.</p>
-<p>When multiple files are specified, Setup will select the one which best matches the system's DPI setting. The recommended size of the bitmap per DPI setting is:</p>
+<p>When multiple files are specified, Setup will automatically select the one which best matches the system's DPI setting. The recommended size of the bitmap per DPI setting is:</p>
 <table>
 <tr><td>100%</td><td>55x55</td></tr>
 <tr><td>125%</td><td>64x68</td></tr>

+ 11 - 4
whatsnew.htm

@@ -29,22 +29,29 @@ For conditions of distribution and use, see <a href="http://www.jrsoftware.org/f
 <p><a name="5.5.10"></a><span class="ver">5.5.10 </span><span class="date">(?)</span></p>
 <ul>
 <li>/LOG: Now logs registry entries.</li>
-<li>Compiler IDE change: Syntax highlighting and syntax error underlining support for <tt>#include</tt> and ISPP has been significantly improved (with ISPP installed: <a href="https://i.imgur.com/nPiun9k.png">before</a> and <a href="https://i.imgur.com/HoJaBRP.png">after</a>, without ISPP installed: <a href="https://i.imgur.com/nPiun9k.png">before</a> and <a href="https://i.imgur.com/ro0ejL3.png">after</a>. Based on a contribution by <a href="https://github.com/KngStr" target="_blank">KngStr</a> via <a href="https://github.com/jrsoftware" target="_blank">GitHub</a>.</li>
-<li>The <tt>WizardImageFile</tt> and <tt>WizardSmallImageFile</tt> [Setup] section directives now may list multiple files. This can be used to include multiple sizes of the wizard images to avoid blurred images on high DPI systems. See the help file for a list of recommended sizes. Note: when you test this be sure to first log out and back in after any DPI change.</li>
-<li><b>Change in default behavior:</b> If [Setup] section directive <tt>DisableWelcomePage</tt> is set (which it is by default), then the title of the wizard now includes <tt>AppVerName</tt> instead of <tt>AppName</tt>, in other words: it now includes the version number of the application. If <tt>WindowVisible</tt> is set to <tt>yes</tt> this applies to the background window instead.</li>
+<li>Compiler IDE change: Syntax highlighting and syntax error underlining support for <tt>#include</tt> and ISPP has been significantly improved (with ISPP installed: <a href="https://i.imgur.com/nPiun9k.png">before</a> and <a href="https://i.imgur.com/HoJaBRP.png">after</a>, without ISPP installed: <a href="https://i.imgur.com/nPiun9k.png">before</a> and <a href="https://i.imgur.com/ro0ejL3.png">after</a></li>
+<li>Improved support for high DPI systems:
+<ul>
+  <li>The <tt>WizardImageFile</tt> and <tt>WizardSmallImageFile</tt> [Setup] section directives now may list multiple files. When multiple files are specified, Setup will automatically select the one which best matches the system's DPI setting. See the help file for a list of recommended sizes. Note: when you test this be sure to first log out and back in after any DPI change.</li>
+  <li>If the <tt>WizardImageStretch</tt> [Setup] section is set to <tt>yes</tt> (which it is by default) then the quality of the stretching that occurs if the best matching wizard image is still larger or smaller than required has been much improved. Also applies to any <tt>TBitmapImage</tt> which uses stretching.</li>
+</ul>
+</li>
+<li><b>Change in default behavior:</b> If [Setup] section directive <tt>DisableWelcomePage</tt> is set to <tt>yes</tt> (which it is by default), then the title of the wizard now includes <tt>AppVerName</tt> instead of <tt>AppName</tt>, in other words: it now includes the version number of the application. If <tt>WindowVisible</tt> is set to <tt>yes</tt> this applies to the background window instead.</li>
 <li>Added new [Setup] section directive: <tt>SignToolRetryDelay</tt>, which defaults to 500. Specifies the number of milliseconds the Setup Compiler should wait before any automatic digital signing retries specified by <tt>SignToolRetryCount</tt>.</li>
 <li>Added new [Setup] section directive: <tt>SignToolMinimumTimeBetween</tt>, which defaults to 0. If not set to 0, specifies the minimum number of milliseconds that should have elapsed between consecutive digital signing actions by the Setup Compiler. For example, if set to 5000 then the Setup Compiler will perform 1 digital signing per 5 seconds at most. Can be used to avoid being rejected by rate limiting timestamp services.</li>
 <li>Added new [Setup] section directive <tt>ArchitecturesAllowed</tt> value: <tt>arm64</tt>. This can be used to not allow Setup to run on Windows 10 on ARM64. Note that Windows 10 on ARM64 only supports 32-bit (x86) binaries and therefore Setup (even in older 3s) will never install in 64-bit mode on Windows 10 on ARM64.</li>
 <li>Pascal Scripting changes:
 <ul>
-  <li>Unicode Inno Setup: Unicode is now supported for the input source. For example, where before you had to write <tt>S := #$0100 + #$0101 + 'Aa';</tt> you can now write <tt>S := '&#x0100;&#x0101;Aa';</tt> directly. Also see the new <i>UnicodeExample1.iss</i> example script. Based on a contribution by <a href="https://github.com/ElSanchez666" target="_blank">ElSanchez</a> via <a href="https://github.com/jrsoftware" target="_blank">GitHub</a>.</li>
+  <li>Unicode Inno Setup: Unicode is now supported for the input source. For example, where before you had to write <tt>S := #$0100 + #$0101 + 'Aa';</tt> you can now write <tt>S := '&#x0100;&#x0101;Aa';</tt> directly. Also see the new <i>UnicodeExample1.iss</i> example script.</li>
   <li>Added new <tt>IsX86</tt>, <tt>IsX64</tt>, <tt>IsIA64</tt>, and <tt>IsARM64</tt> support functions.</li>
 </ul>
+</li>
 <li>Any [Files] entries with the <tt>deleteafterinstall</tt> flag or with <tt>DestDir</tt> set to <tt>{tmp}</tt> or a subdirectory of <tt>{tmp}</tt> are no longer included in the <tt>EstimatedSize</tt> value in the Uninstall registry key.</li>
 <li><i>Fix:</i> In 5.5.9 it was no longer possible to include a drive colon in [Setup] section directive <tt>OutputManifestFile</tt>.</li>
 <li>Unicode [Code] based on RemObjects Pascal Script Git commit 4b310a0fe3905b8b1b6004a2891a8388a3fb3c1c.</li>
 <li>Minor tweaks.</li>
 </ul>
+<p>Thanks to ElSanchez and DRON for their contributions for Unicode input source support and improved image stretching respectively. Thanks to KngStr for his proof of concept for ISPP compiler directive highlighting.<p>
 
 <p><a name="5.5.9"></a><span class="ver">5.5.9 </span><span class="date">(2016-04-06)</span></p>
 <ul>