public unsafe override BitmapBase Apply(RenderTask renderTask, BitmapBase layer) { Tank tank = renderTask.Tank; var bounds = Mode == ClipMode.ByPixels ? layer.PreciseSize(PixelAlphaThreshold) : Mode == ClipMode.ByLayerBounds ? PixelRect.FromMixed(0, 0, layer.Width, layer.Height) : PixelRect.FromMixed(0, 0, Layer.ParentStyle.IconWidth, Layer.ParentStyle.IconHeight); using (layer.UseWrite()) { int count; byte *ptr; // Clip Top for (int y = 0; y < bounds.Top + ClipTop && y < layer.Height; y++) { Ut.MemSet(layer.Data + y * layer.Stride, 0, layer.Width * 4); } // Clip Bottom for (int y = layer.Height - 1; y > bounds.Bottom - ClipBottom && y >= 0; y--) { Ut.MemSet(layer.Data + y * layer.Stride, 0, layer.Width * 4); } // Clip Left count = Math.Min(bounds.Left + ClipLeft, layer.Width) * 4; ptr = layer.Data; if (count > 0) { for (int y = 0; y < layer.Height; y++, ptr += layer.Stride) { Ut.MemSet(ptr, 0, count); } } // Clip Right count = Math.Min(layer.Width - 1 - bounds.Right + ClipRight, layer.Width) * 4; ptr = layer.Data + layer.Width * 4 - count; if (count > 0) { for (int y = 0; y < layer.Height; y++, ptr += layer.Stride) { Ut.MemSet(ptr, 0, count); } } } return(layer); }
public unsafe override BitmapBase Apply(Tank tank, BitmapBase layer) { using (layer.UseWrite()) { // Just scale the brightness and alpha channels so as to normalize the maximum value. // This is crude but gives good results; a better algorithm would try to fit the histogram // to a predefined standard by scaling non-linearly. double maxBrightness = -1; double maxAlpha = -1; for (int y = 0; y < layer.Height; y++) { byte* ptr = layer.Data + y * layer.Stride; byte* end = ptr + layer.Width * 4; while (ptr < end) { byte alpha = *(ptr + 3); if (alpha > 0) // there are a lot of non-black pixels in the fully-transparent regions { if (NormalizeBrightness) { double brightness = *(ptr + 0) * 0.0722 + *(ptr + 1) * 0.7152 + *(ptr + 2) * 0.2126; if (brightness > maxBrightness) maxBrightness = brightness; } if (NormalizeAlpha) { if (alpha > maxAlpha) maxAlpha = alpha; } } ptr += 4; } } double scaleBrightness = (double) MaxBrightness / maxBrightness; double scaleAlpha = (double) MaxAlpha / maxAlpha; for (int y = 0; y < layer.Height; y++) { byte* ptr = layer.Data + y * layer.Stride; byte* end = ptr + layer.Width * 4; while (ptr < end) { byte alpha = *(ptr + 3); if (alpha > 0) { if (NormalizeBrightness) { if (Grayscale) { double brightness = *(ptr + 0) * 0.0722 + *(ptr + 1) * 0.7152 + *(ptr + 2) * 0.2126; *(ptr + 0) = *(ptr + 1) = *(ptr + 2) = (byte) (brightness * scaleBrightness).ClipMax(255); } else { // TODO: the clipping here alters the hue. Ideally the color should be clipped without altering hue, by increasing brightness until white. *(ptr + 0) = (byte) (*(ptr + 0) * scaleBrightness).ClipMax(255); *(ptr + 1) = (byte) (*(ptr + 1) * scaleBrightness).ClipMax(255); *(ptr + 2) = (byte) (*(ptr + 2) * scaleBrightness).ClipMax(255); } } else if (Grayscale) { double brightness = *(ptr + 0) * 0.0722 + *(ptr + 1) * 0.7152 + *(ptr + 2) * 0.2126; *(ptr + 0) = *(ptr + 1) = *(ptr + 2) = (byte) brightness; } if (NormalizeAlpha) { *(ptr + 3) = (byte) (alpha * scaleAlpha); } } ptr += 4; } } } return layer; }
public unsafe override BitmapBase Apply(RenderTask renderTask, BitmapBase layer) { Tank tank = renderTask.Tank; using (layer.UseWrite()) { // Just scale the brightness and alpha channels so as to normalize the maximum value. // This is crude but gives good results; a better algorithm would try to fit the histogram // to a predefined standard by scaling non-linearly. double maxBrightness = -1; double maxAlpha = -1; for (int y = 0; y < layer.Height; y++) { byte *ptr = layer.Data + y * layer.Stride; byte *end = ptr + layer.Width * 4; while (ptr < end) { byte alpha = *(ptr + 3); if (alpha > 0) // there are a lot of non-black pixels in the fully-transparent regions { if (NormalizeBrightness) { double brightness = *(ptr + 0) * 0.0722 + *(ptr + 1) * 0.7152 + *(ptr + 2) * 0.2126; if (brightness > maxBrightness) { maxBrightness = brightness; } } if (NormalizeAlpha) { if (alpha > maxAlpha) { maxAlpha = alpha; } } } ptr += 4; } } double scaleBrightness = (double)MaxBrightness / maxBrightness; double scaleAlpha = (double)MaxAlpha / maxAlpha; for (int y = 0; y < layer.Height; y++) { byte *ptr = layer.Data + y * layer.Stride; byte *end = ptr + layer.Width * 4; while (ptr < end) { byte alpha = *(ptr + 3); if (alpha > 0) { if (NormalizeBrightness) { if (Grayscale) { double brightness = *(ptr + 0) * 0.0722 + *(ptr + 1) * 0.7152 + *(ptr + 2) * 0.2126; *(ptr + 0) = *(ptr + 1) = *(ptr + 2) = (byte)(brightness * scaleBrightness).ClipMax(255); } else { // TODO: the clipping here alters the hue. Ideally the color should be clipped without altering hue, by increasing brightness until white. *(ptr + 0) = (byte)(*(ptr + 0) * scaleBrightness).ClipMax(255); *(ptr + 1) = (byte)(*(ptr + 1) * scaleBrightness).ClipMax(255); *(ptr + 2) = (byte)(*(ptr + 2) * scaleBrightness).ClipMax(255); } } else if (Grayscale) { double brightness = *(ptr + 0) * 0.0722 + *(ptr + 1) * 0.7152 + *(ptr + 2) * 0.2126; *(ptr + 0) = *(ptr + 1) = *(ptr + 2) = (byte)brightness; } if (NormalizeAlpha) { *(ptr + 3) = (byte)(alpha * scaleAlpha); } } ptr += 4; } } } return(layer); }
public unsafe override BitmapBase Apply(RenderTask renderTask, BitmapBase layer) { Tank tank = renderTask.Tank; LayerBase maskLayer; if (string.IsNullOrEmpty(MaskLayerId)) { return(layer); } maskLayer = renderTask.Style.Layers.FirstOrDefault(x => x.Id == MaskLayerId); if (maskLayer == null) { throw new StyleUserError(App.Translation.EffectMaskLayer.ErrorInvalidId.Fmt(MaskLayerId)); } if (renderTask.IsLayerAlreadyReferenced(maskLayer)) { throw new StyleUserError(App.Translation.EffectMaskLayer.ErrorRecursiveLayerReference.Fmt(MaskLayerId)); } var maskImg = renderTask.RenderLayer(maskLayer); using (layer.UseWrite()) { using (maskImg.UseRead()) { for (int i = 0; i < layer.Width; ++i) { for (int j = 0; j < layer.Height; ++j) { decimal alpha = 0; if (i < maskImg.Width && j < maskImg.Height) { switch (MaskMode) { case Effects.MaskMode.Opacity: alpha = maskImg.Data[i * 4 + maskImg.Stride * j + 3]; break; case Effects.MaskMode.Grayscale: alpha = (maskImg.Data[i * 4 + maskImg.Stride * j] + maskImg.Data[i * 4 + maskImg.Stride * j + 1] + maskImg.Data[i * 4 + maskImg.Stride * j + 2] ) / 3; break; case Effects.MaskMode.Combined: alpha = (maskImg.Data[i * 4 + maskImg.Stride * j] + maskImg.Data[i * 4 + maskImg.Stride * j + 1] + maskImg.Data[i * 4 + maskImg.Stride * j + 2] ) / 3 * maskImg.Data[i * 4 + maskImg.Stride * j + 3] / 255m; break; } } if (Invert) { alpha = 255m - alpha; } var opacity = layer.Data[i * 4 + layer.Stride * j + 3] * (alpha / 255m); layer.Data[i * 4 + layer.Stride * j + 3] = (byte)opacity; } } } } return(layer); }
public unsafe override BitmapBase Apply(Tank tank, BitmapBase layer) { var bounds = Mode == ClipMode.ByPixels ? layer.PreciseSize(PixelAlphaThreshold) : Mode == ClipMode.ByLayerBounds ? PixelRect.FromMixed(0, 0, layer.Width, layer.Height) : PixelRect.FromMixed(0, 0, Layer.ParentStyle.IconWidth, Layer.ParentStyle.IconHeight); using (layer.UseWrite()) { int count; byte* ptr; // Clip Top for (int y = 0; y < bounds.Top + ClipTop && y < layer.Height; y++) Ut.MemSet(layer.Data + y * layer.Stride, 0, layer.Width * 4); // Clip Bottom for (int y = layer.Height - 1; y > bounds.Bottom - ClipBottom && y >= 0; y--) Ut.MemSet(layer.Data + y * layer.Stride, 0, layer.Width * 4); // Clip Left count = Math.Min(bounds.Left + ClipLeft, layer.Width) * 4; ptr = layer.Data; if (count > 0) for (int y = 0; y < layer.Height; y++, ptr += layer.Stride) Ut.MemSet(ptr, 0, count); // Clip Right count = Math.Min(layer.Width - 1 - bounds.Right + ClipRight, layer.Width) * 4; ptr = layer.Data + layer.Width * 4 - count; if (count > 0) for (int y = 0; y < layer.Height; y++, ptr += layer.Stride) Ut.MemSet(ptr, 0, count); } return layer; }