diff --git a/OpenRA.Game/Graphics/Sheet.cs b/OpenRA.Game/Graphics/Sheet.cs index 0c56c15bb2..2cb7643afd 100644 --- a/OpenRA.Game/Graphics/Sheet.cs +++ b/OpenRA.Game/Graphics/Sheet.cs @@ -49,15 +49,9 @@ namespace OpenRA.Graphics using (var bitmap = (Bitmap)Image.FromStream(stream)) { Size = bitmap.Size; + data = new byte[4 * Size.Width * Size.Height]; - var dataStride = 4 * Size.Width; - data = new byte[dataStride * Size.Height]; - - var bd = bitmap.LockBits(bitmap.Bounds(), - ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb); - for (var y = 0; y < Size.Height; y++) - Marshal.Copy(IntPtr.Add(bd.Scan0, y * bd.Stride), data, y * dataStride, dataStride); - bitmap.UnlockBits(bd); + Util.FastCopyIntoSprite(new Sprite(this, bitmap.Bounds(), TextureChannel.Red), bitmap); } ReleaseBuffer(); diff --git a/OpenRA.Game/Graphics/SpriteFont.cs b/OpenRA.Game/Graphics/SpriteFont.cs index 8a3c8857c3..804c633b63 100644 --- a/OpenRA.Game/Graphics/SpriteFont.cs +++ b/OpenRA.Game/Graphics/SpriteFont.cs @@ -114,6 +114,7 @@ namespace OpenRA.Graphics // A new bitmap is generated each time this property is accessed, so we do need to dispose it. using (var bitmap = face.Glyph.Bitmap) + { unsafe { var p = (byte*)bitmap.Buffer; @@ -123,18 +124,23 @@ namespace OpenRA.Graphics for (var j = 0; j < s.Size.Y; j++) { for (var i = 0; i < s.Size.X; i++) + { if (p[i] != 0) { var q = destStride * (j + s.Bounds.Top) + 4 * (i + s.Bounds.Left); - dest[q] = c.Second.B; - dest[q + 1] = c.Second.G; - dest[q + 2] = c.Second.R; - dest[q + 3] = p[i]; + var pmc = Util.PremultiplyAlpha(Color.FromArgb(p[i], c.Second)); + + dest[q] = pmc.B; + dest[q + 1] = pmc.G; + dest[q + 2] = pmc.R; + dest[q + 3] = pmc.A; } + } p += bitmap.Pitch; } } + } s.Sheet.CommitBufferedData(); diff --git a/OpenRA.Game/Graphics/Util.cs b/OpenRA.Game/Graphics/Util.cs index e64c452e80..166f117cea 100644 --- a/OpenRA.Game/Graphics/Util.cs +++ b/OpenRA.Game/Graphics/Util.cs @@ -73,18 +73,37 @@ namespace OpenRA.Graphics try { - var data = dest.Sheet.GetData(); - var dataStride = dest.Sheet.Size.Width * 4; - var x = dest.Bounds.Left * 4; - var width = dest.Bounds.Width * 4; - var y = dest.Bounds.Top; + var destData = dest.Sheet.GetData(); + var destStride = dest.Sheet.Size.Width; + var width = dest.Bounds.Width; var height = dest.Bounds.Height; - var bd = src.LockBits(src.Bounds(), - ImageLockMode.ReadWrite, src.PixelFormat); - for (var row = 0; row < height; row++) - Marshal.Copy(IntPtr.Add(bd.Scan0, row * bd.Stride), data, (y + row) * dataStride + x, width); - src.UnlockBits(bd); + var srcData = src.LockBits(src.Bounds(), + ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb); + + unsafe + { + var c = (int*)srcData.Scan0; + + // Cast the data to an int array so we can copy the src data directly + fixed (byte* bd = &destData[0]) + { + var data = (int*)bd; + var x = dest.Bounds.Left; + var y = dest.Bounds.Top; + + for (var j = 0; j < height; j++) + { + for (var i = 0; i < width; i++) + { + var cc = Color.FromArgb(*(c + (j * srcData.Stride >> 2) + i)); + data[(y + j) * destStride + x + i] = PremultiplyAlpha(cc).ToArgb(); + } + } + } + } + + src.UnlockBits(srcData); } finally { @@ -93,6 +112,12 @@ namespace OpenRA.Graphics } } + public static Color PremultiplyAlpha(Color c) + { + var a = c.A / 255f; + return Color.FromArgb(c.A, (byte)(c.R * a + 0.5f), (byte)(c.G * a + 0.5f), (byte)(c.B * a + 0.5f)); + } + public static float[] IdentityMatrix() { return Exts.MakeArray(16, j => (j % 5 == 0) ? 1.0f : 0); diff --git a/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj b/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj index cc1b29a043..a7ddefc2a4 100644 --- a/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj +++ b/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj @@ -643,6 +643,7 @@ + diff --git a/OpenRA.Mods.Common/Traits/World/PaletteFromPaletteWithAlpha.cs b/OpenRA.Mods.Common/Traits/World/PaletteFromPaletteWithAlpha.cs new file mode 100644 index 0000000000..b6b9de1027 --- /dev/null +++ b/OpenRA.Mods.Common/Traits/World/PaletteFromPaletteWithAlpha.cs @@ -0,0 +1,76 @@ +#region Copyright & License Information +/* + * Copyright 2007-2015 The OpenRA Developers (see AUTHORS) + * This file is part of OpenRA, which is free software. It is made + * available to you under the terms of the GNU General Public License + * as published by the Free Software Foundation. For more information, + * see COPYING. + */ +#endregion + +using System.Collections.Generic; +using System.Drawing; +using OpenRA.Graphics; +using OpenRA.Mods.Common.Traits; +using OpenRA.Traits; + +namespace OpenRA.Mods.D2k.Traits +{ + [Desc("Create a palette by applying alpha transparency to another palette.")] + class PaletteFromPaletteWithAlphaInfo : ITraitInfo + { + [Desc("Internal palette name")] + public readonly string Name = null; + + [Desc("The name of the palette to base off.")] + public readonly string BasePalette = null; + + [Desc("Allow palette modifiers to change the palette.")] + public readonly bool AllowModifiers = true; + + [Desc("Alpha component that is applied to the base palette.")] + public readonly float Alpha = 1.0f; + + [Desc("Premultiply color by the alpha component.")] + public readonly bool Premultiply = true; + + public object Create(ActorInitializer init) { return new PaletteFromPaletteWithAlpha(this); } + } + + class PaletteFromPaletteWithAlpha : ILoadsPalettes, IProvidesAssetBrowserPalettes + { + readonly PaletteFromPaletteWithAlphaInfo info; + + public PaletteFromPaletteWithAlpha(PaletteFromPaletteWithAlphaInfo info) { this.info = info; } + + public void LoadPalettes(WorldRenderer wr) + { + var remap = new AlphaPaletteRemap(info.Alpha, info.Premultiply); + wr.AddPalette(info.Name, new ImmutablePalette(wr.Palette(info.BasePalette).Palette, remap), info.AllowModifiers); + } + + public IEnumerable PaletteNames { get { yield return info.Name; } } + } + + class AlphaPaletteRemap : IPaletteRemap + { + readonly float alpha; + readonly bool premultiply; + + public AlphaPaletteRemap(float alpha, bool premultiply) + { + this.alpha = alpha; + this.premultiply = premultiply; + } + + public Color GetRemappedColor(Color original, int index) + { + var a = (int)(original.A * alpha).Clamp(0, 255); + var r = premultiply ? (int)(alpha * original.R + 0.5f).Clamp(0, 255) : original.R; + var g = premultiply ? (int)(alpha * original.G + 0.5f).Clamp(0, 255) : original.G; + var b = premultiply ? (int)(alpha * original.B + 0.5f).Clamp(0, 255) : original.B; + + return Color.FromArgb(a, r, g, b); + } + } +} diff --git a/OpenRA.Mods.Common/Traits/World/PaletteFromRGBA.cs b/OpenRA.Mods.Common/Traits/World/PaletteFromRGBA.cs index 055ee5944a..ecdf275618 100644 --- a/OpenRA.Mods.Common/Traits/World/PaletteFromRGBA.cs +++ b/OpenRA.Mods.Common/Traits/World/PaletteFromRGBA.cs @@ -8,6 +8,7 @@ */ #endregion +using System.Drawing; using System.Linq; using OpenRA.Graphics; using OpenRA.Traits; @@ -50,7 +51,11 @@ namespace OpenRA.Mods.Common.Traits if (info.Tileset != null && info.Tileset.ToLowerInvariant() != world.Map.Tileset.ToLowerInvariant()) return; - var c = (uint)((info.A << 24) | (info.R << 16) | (info.G << 8) | info.B); + var a = info.A / 255f; + var r = (int)(a * info.R + 0.5f).Clamp(0, 255); + var g = (int)(a * info.G + 0.5f).Clamp(0, 255); + var b = (int)(a * info.B + 0.5f).Clamp(0, 255); + var c = (uint)Color.FromArgb(info.A, r, g, b).ToArgb(); wr.AddPalette(info.Name, new ImmutablePalette(Enumerable.Range(0, Palette.Size).Select(i => (i == 0) ? 0 : c)), info.AllowModifiers); } } diff --git a/OpenRA.Mods.Common/Traits/World/ShroudPalette.cs b/OpenRA.Mods.Common/Traits/World/ShroudPalette.cs index eca4002cbb..98810c6be1 100644 --- a/OpenRA.Mods.Common/Traits/World/ShroudPalette.cs +++ b/OpenRA.Mods.Common/Traits/World/ShroudPalette.cs @@ -42,8 +42,8 @@ namespace OpenRA.Mods.Common.Traits static readonly Color[] Fog = new[] { - Color.Transparent, Color.Green, - Color.Blue, Color.Yellow, + Color.FromArgb(0, 0, 0, 0), + Color.Green, Color.Blue, Color.Yellow, Color.FromArgb(128, 0, 0, 0), Color.FromArgb(96, 0, 0, 0), Color.FromArgb(64, 0, 0, 0), @@ -52,8 +52,8 @@ namespace OpenRA.Mods.Common.Traits static readonly Color[] Shroud = new[] { - Color.Transparent, Color.Green, - Color.Blue, Color.Yellow, + Color.FromArgb(0, 0, 0, 0), + Color.Green, Color.Blue, Color.Yellow, Color.Black, Color.FromArgb(160, 0, 0, 0), Color.FromArgb(128, 0, 0, 0), diff --git a/OpenRA.Renderer.Sdl2/Sdl2GraphicsDevice.cs b/OpenRA.Renderer.Sdl2/Sdl2GraphicsDevice.cs index b9b1c9142f..691ba5b972 100755 --- a/OpenRA.Renderer.Sdl2/Sdl2GraphicsDevice.cs +++ b/OpenRA.Renderer.Sdl2/Sdl2GraphicsDevice.cs @@ -259,7 +259,7 @@ namespace OpenRA.Renderer.Sdl2 case BlendMode.Alpha: GL.Enable(EnableCap.Blend); ErrorHandler.CheckGlError(); - GL.BlendFunc(BlendingFactorSrc.SrcAlpha, BlendingFactorDest.OneMinusSrcAlpha); + GL.BlendFunc(BlendingFactorSrc.One, BlendingFactorDest.OneMinusSrcAlpha); break; case BlendMode.Additive: GL.Enable(EnableCap.Blend); diff --git a/mods/d2k/rules/palettes.yaml b/mods/d2k/rules/palettes.yaml index fca79be2d3..c6b67f8d35 100644 --- a/mods/d2k/rules/palettes.yaml +++ b/mods/d2k/rules/palettes.yaml @@ -49,6 +49,14 @@ G: 0 B: 0 A: 180 + PaletteFromPaletteWithAlpha@effect75alpha: + Name: effect75alpha + BasePalette: effect + Alpha: 0.75 + PaletteFromPaletteWithAlpha@effect50alpha: + Name: effect50alpha + BasePalette: effect + Alpha: 0.5 PaletteFromScaledPalette@starportlights: Name: starportlights BasePalette: d2k diff --git a/mods/d2k/rules/vehicles.yaml b/mods/d2k/rules/vehicles.yaml index 1c178d084d..cb4e740af6 100644 --- a/mods/d2k/rules/vehicles.yaml +++ b/mods/d2k/rules/vehicles.yaml @@ -81,6 +81,7 @@ harvester: LeavesHusk: HuskActor: Harvester.Husk WithHarvestAnimation: + Palette: effect50alpha AttractsWorms: Intensity: 700 diff --git a/mods/d2k/sequences/misc.yaml b/mods/d2k/sequences/misc.yaml index 6cfa4c58fa..7952651246 100644 --- a/mods/d2k/sequences/misc.yaml +++ b/mods/d2k/sequences/misc.yaml @@ -71,30 +71,24 @@ small_trail: Start: 3735 Length: 4 Tick: 80 - BlendMode: SoftAdditive small_trail2: idle: DATA.R8 Start: 3540 Length: 4 Tick: 80 - BlendMode: SoftAdditive bazooka_trail: idle: DATA.R8 Start: 3381 Length: 4 Tick: 80 - BlendMode: Translucency - Alpha: 0.75 bazooka_trail2: idle: DATA.R8 Start: 3544 Length: 4 Tick: 80 - BlendMode: Translucency - Alpha: 0.75 deviator_trail: idle: DATA.R8 diff --git a/mods/d2k/sequences/vehicles.yaml b/mods/d2k/sequences/vehicles.yaml index 9b2357e6f5..75bab9d93b 100644 --- a/mods/d2k/sequences/vehicles.yaml +++ b/mods/d2k/sequences/vehicles.yaml @@ -23,7 +23,6 @@ harvester: Tick: 80 ZOffset: 1 BlendMode: Multiply - Alpha: 0.5 dock: DATA.R8 Start: 3370 Length: 10 diff --git a/mods/d2k/weapons.yaml b/mods/d2k/weapons.yaml index f041ede50a..107f3f76d8 100644 --- a/mods/d2k/weapons.yaml +++ b/mods/d2k/weapons.yaml @@ -34,6 +34,7 @@ Bazooka: Image: RPG RateOfTurn: 5 Trail: bazooka_trail + TrailPalette: effect75alpha TrailInterval: 1 RangeLimit: 35 Warhead@1Dam: SpreadDamage @@ -193,6 +194,7 @@ QuadRockets: Image: RPG RateOfTurn: 10 Trail: bazooka_trail2 + TrailPalette: effect75alpha TrailInterval: 1 Speed: 256 RangeLimit: 40 diff --git a/mods/ts/rules/palettes.yaml b/mods/ts/rules/palettes.yaml index bc35356e18..013ae575cf 100644 --- a/mods/ts/rules/palettes.yaml +++ b/mods/ts/rules/palettes.yaml @@ -73,6 +73,10 @@ G: 0 B: 0 A: 180 + PaletteFromPaletteWithAlpha@placebuilding: + Name: placebuilding + BasePalette: ra + Alpha: 0.75 TSShroudPalette@shroud: Type: Shroud VoxelNormalsPalette@normals: diff --git a/mods/ts/rules/player.yaml b/mods/ts/rules/player.yaml index 10d090501d..65af07b43e 100644 --- a/mods/ts/rules/player.yaml +++ b/mods/ts/rules/player.yaml @@ -30,7 +30,7 @@ Player: LowPowerSlowdown: 3 SpeedUp: True PlaceBuilding: - Palette: ra + Palette: placebuilding SupportPowerManager: ScriptTriggers: MissionObjectives: diff --git a/mods/ts/sequences/misc.yaml b/mods/ts/sequences/misc.yaml index 678e141e28..9d23320193 100644 --- a/mods/ts/sequences/misc.yaml +++ b/mods/ts/sequences/misc.yaml @@ -1,8 +1,6 @@ overlay: Defaults: place Offset: 0, -12 - BlendMode: Translucency - Alpha: 0.75 build-valid-snow: build-valid-temperat: build-invalid: @@ -114,10 +112,10 @@ explosion: Length: * building: twlt070 ionring: ring1 - BlendMode: SoftAdditive + BlendMode: Additive Tick: 120 pulse_explosion: pulsefx2 - BlendMode: SoftAdditive + BlendMode: Additive Tick: 80 small_watersplash: h2o_exp2 large_watersplash: h2o_exp1