public void Blend(IarImage overlay) { int pixel_size = Info.Stride / (int)Info.Width; if (pixel_size < 4) { return; } var self = new Rectangle(-Info.OffsetX, -Info.OffsetY, (int)Info.Width, (int)Info.Height); var src = new Rectangle(-overlay.Info.OffsetX, -overlay.Info.OffsetY, (int)overlay.Info.Width, (int)overlay.Info.Height); var blend = Rectangle.Intersect(self, src); if (blend.IsEmpty) { return; } src.X = blend.Left - src.Left; src.Y = blend.Top - src.Top; src.Width = blend.Width; src.Height = blend.Height; if (src.Width <= 0 || src.Height <= 0) { return; } int x = blend.Left - self.Left; int y = blend.Top - self.Top; int dst = y * Info.Stride + x * pixel_size; int ov = src.Top * overlay.Info.Stride + src.Left * pixel_size; for (int row = 0; row < src.Height; ++row) { for (int col = 0; col < src.Width; ++col) { int src_pixel = ov + col * pixel_size; int src_alpha = overlay.Data[src_pixel + 3]; if (src_alpha > 0) { int dst_pixel = dst + col * pixel_size; if (0xFF == src_alpha || 0 == m_output[dst_pixel + 3]) { Buffer.BlockCopy(overlay.Data, src_pixel, m_output, dst_pixel, pixel_size); } else { m_output[dst_pixel + 0] = (byte)((overlay.Data[src_pixel + 0] * src_alpha + m_output[dst_pixel + 0] * (0xFF - src_alpha)) / 0xFF); m_output[dst_pixel + 1] = (byte)((overlay.Data[src_pixel + 1] * src_alpha + m_output[dst_pixel + 1] * (0xFF - src_alpha)) / 0xFF); m_output[dst_pixel + 2] = (byte)((overlay.Data[src_pixel + 2] * src_alpha + m_output[dst_pixel + 2] * (0xFF - src_alpha)) / 0xFF); m_output[dst_pixel + 3] = (byte)Math.Max(src_alpha, m_output[dst_pixel + 3]); } } } dst += Info.Stride; ov += overlay.Info.Stride; } }
IarImage CombineImage(IarImage overlay, IarArchive iarc) { using (var input = new BinMemoryStream(overlay.Data)) { var dir = (List <Entry>)iarc.Dir; int base_index = input.ReadInt32(); if (base_index >= dir.Count) { throw new InvalidFormatException("Invalid base image index"); } int diff_y = input.ReadInt32(); int diff_count = input.ReadInt32(); var overlay_info = overlay.Info; int pixel_size = overlay_info.BPP / 8; var base_image = new IarImage(iarc, dir[base_index]); byte[] output = base_image.Data; if (overlay_info.Height != base_image.Info.Height || overlay_info.Stride != base_image.Info.Stride) { int src_height = (int)Math.Min(overlay_info.Height, base_image.Info.Height); int src_stride = Math.Min(overlay_info.Stride, base_image.Info.Stride); byte[] src = base_image.Data; output = new byte[overlay_info.Height * overlay_info.Stride]; int dst_pos = 0; if (base_image.Info.OffsetY < overlay_info.OffsetY) { dst_pos += (-base_image.Info.OffsetY + overlay_info.OffsetY) * overlay_info.Stride; } if (base_image.Info.OffsetX < overlay_info.OffsetX) { dst_pos += (-base_image.Info.OffsetX + overlay_info.OffsetX) * pixel_size; } for (int y = 0; y < src_height; ++y) { Buffer.BlockCopy(src, y * base_image.Info.Stride, output, dst_pos, src_stride); dst_pos += overlay_info.Stride; } } int dst = diff_y * overlay_info.Stride; for (int i = 0; i < diff_count; ++i) { int chunk_count = input.ReadUInt16(); int x = 0; for (int j = 0; j < chunk_count; ++j) { int skip_count = pixel_size * input.ReadUInt16(); int copy_count = pixel_size * input.ReadUInt16(); x += skip_count; input.Read(output, dst + x, copy_count); x += copy_count; } dst += overlay_info.Stride; } return(new IarImage(overlay_info, output, overlay.Palette)); } }
public override Stream OpenEntry(ArcFile arc, Entry entry) { var iarc = arc as IarArchive; if (null == iarc) { return(base.OpenEntry(arc, entry)); } try { int flags = arc.File.View.ReadUInt16(entry.Offset); var image = new IarImage(iarc, entry); if (0 != (flags & 0x1000)) { image = CombineLayers(image, iarc); } else if (0 != (flags & 0x800)) { image = CombineImage(image, iarc); } if (null == image) { return(base.OpenEntry(arc, entry)); } // internal 'IAR SAS5' format var header = new byte[0x28 + image.Info.PaletteSize]; using (var mem = new MemoryStream(header)) using (var writer = new BinaryWriter(mem)) { writer.Write(0x00524149); // 'IAR' writer.Write(0x35534153); // 'SAS5' writer.Write(image.Info.Width); writer.Write(image.Info.Height); writer.Write(image.Info.OffsetX); writer.Write(image.Info.OffsetY); writer.Write(image.Info.BPP); writer.Write(image.Info.Stride); writer.Write(image.Info.PaletteSize); writer.Write(image.Data.Length); if (null != image.Palette) { writer.Write(image.Palette, 0, image.Palette.Length); } return(new PrefixStream(header, new MemoryStream(image.Data))); } } catch (Exception X) { Trace.WriteLine(X.Message, entry.Name); return(base.OpenEntry(arc, entry)); } }
IarImage CombineLayers(IarImage layers, IarArchive iarc) { layers.Info.Stride = (int)layers.Info.Width * 4; layers.Info.BPP = 32; var pixels = new byte[layers.Info.Stride * (int)layers.Info.Height]; var output = new IarImage(layers.Info, pixels); using (var mem = new MemoryStream(layers.Data)) using (var input = new BinaryReader(mem)) { int offset_x = 0, offset_y = 0; var dir = (List <Entry>)iarc.Dir; while (input.BaseStream.Position < input.BaseStream.Length) { int cmd = input.ReadByte(); switch (cmd) { case 0x21: offset_x += input.ReadInt16(); offset_y += input.ReadInt16(); break; case 0x00: case 0x20: { int index = input.ReadInt32(); if (index < 0 || index >= dir.Count) { throw new InvalidFormatException("Invalid image layer index"); } var layer = new IarImage(iarc, dir[index]); layer.Info.OffsetX -= offset_x; layer.Info.OffsetY -= offset_y; if (0x20 == cmd) { output.ApplyMask(layer); } else { output.Blend(layer); } } break; default: Trace.WriteLine(string.Format("Unknown layer type 0x{0:X2}", cmd), "IAR"); break; } } return(output); } }
public void ApplyMask(IarImage mask) { int pixel_size = Info.Stride / (int)Info.Width; if (pixel_size < 4 || mask.Info.BPP != 8) { return; } var self = new Rectangle(-Info.OffsetX, -Info.OffsetY, (int)Info.Width, (int)Info.Height); var mask_region = new Rectangle(-mask.Info.OffsetX, -mask.Info.OffsetY, (int)mask.Info.Width, (int)mask.Info.Height); var masked = Rectangle.Intersect(self, mask_region); if (masked.IsEmpty) { return; } mask_region.X = masked.Left - mask_region.Left; mask_region.Y = masked.Top - mask_region.Top; mask_region.Width = masked.Width; mask_region.Height = masked.Height; if (mask_region.Width <= 0 || mask_region.Height <= 0) { return; } int x = masked.Left - self.Left; int y = masked.Top - self.Top; int dst = y * Info.Stride + x * pixel_size; int src = mask_region.Top * mask.Info.Stride + mask_region.Left; for (int row = 0; row < mask_region.Height; ++row) { int dst_pixel = dst + 3; for (int col = 0; col < mask_region.Width; ++col) { m_output[dst_pixel] = mask.Data[src + col]; dst_pixel += pixel_size; } dst += Info.Stride; src += mask.Info.Stride; } }
public override Stream OpenEntry(ArcFile arc, Entry entry) { var iarc = arc as IarArchive; if (null == iarc) return base.OpenEntry (arc, entry); try { int flags = arc.File.View.ReadUInt16 (entry.Offset); var image = new IarImage (iarc, entry); if (0 != (flags & 0x1000)) image = CombineLayers (image, iarc); else if (0 != (flags & 0x800)) image = CombineImage (image, iarc); if (null == image) return base.OpenEntry (arc, entry); // internal 'IAR SAS5' format var header = new byte[0x28+image.Info.PaletteSize]; using (var mem = new MemoryStream (header)) using (var writer = new BinaryWriter (mem)) { writer.Write (0x00524149); // 'IAR' writer.Write (0x35534153); // 'SAS5' writer.Write (image.Info.Width); writer.Write (image.Info.Height); writer.Write (image.Info.OffsetX); writer.Write (image.Info.OffsetY); writer.Write (image.Info.BPP); writer.Write (image.Info.Stride); writer.Write (image.Info.PaletteSize); writer.Write (image.Data.Length); if (null != image.Palette) writer.Write (image.Palette, 0, image.Palette.Length); return new PrefixStream (header, new MemoryStream (image.Data)); } } catch (Exception X) { Trace.WriteLine (X.Message, entry.Name); return base.OpenEntry (arc, entry); } }
public void Blend(IarImage overlay) { int pixel_size = Info.Stride / (int)Info.Width; if (pixel_size < 4) return; var self = new Rectangle (-Info.OffsetX, -Info.OffsetY, (int)Info.Width, (int)Info.Height); var src = new Rectangle (-overlay.Info.OffsetX, -overlay.Info.OffsetY, (int)overlay.Info.Width, (int)overlay.Info.Height); var blend = Rectangle.Intersect (self, src); if (blend.IsEmpty) return; src.X = blend.Left - src.Left; src.Y = blend.Top - src.Top; src.Width = blend.Width; src.Height= blend.Height; if (src.Width <= 0 || src.Height <= 0) return; int x = blend.Left - self.Left; int y = blend.Top - self.Top; int dst = y * Info.Stride + x * pixel_size; int ov = src.Top * overlay.Info.Stride + src.Left * pixel_size; for (int row = 0; row < src.Height; ++row) { for (int col = 0; col < src.Width; ++col) { int src_pixel = ov + col*pixel_size; int src_alpha = overlay.Data[src_pixel+3]; if (src_alpha > 0) { int dst_pixel = dst + col*pixel_size; if (0xFF == src_alpha || 0 == m_output[dst_pixel+3]) { Buffer.BlockCopy (overlay.Data, src_pixel, m_output, dst_pixel, pixel_size); } else { m_output[dst_pixel+0] = (byte)((overlay.Data[src_pixel+0] * src_alpha + m_output[dst_pixel+0] * (0xFF - src_alpha)) / 0xFF); m_output[dst_pixel+1] = (byte)((overlay.Data[src_pixel+1] * src_alpha + m_output[dst_pixel+1] * (0xFF - src_alpha)) / 0xFF); m_output[dst_pixel+2] = (byte)((overlay.Data[src_pixel+2] * src_alpha + m_output[dst_pixel+2] * (0xFF - src_alpha)) / 0xFF); m_output[dst_pixel+3] = (byte)Math.Max (src_alpha, m_output[dst_pixel+3]); } } } dst += Info.Stride; ov += overlay.Info.Stride; } }
public void ApplyMask(IarImage mask) { int pixel_size = Info.Stride / (int)Info.Width; if (pixel_size < 4 || mask.Info.BPP != 8) return; var self = new Rectangle (-Info.OffsetX, -Info.OffsetY, (int)Info.Width, (int)Info.Height); var mask_region = new Rectangle (-mask.Info.OffsetX, -mask.Info.OffsetY, (int)mask.Info.Width, (int)mask.Info.Height); var masked = Rectangle.Intersect (self, mask_region); if (masked.IsEmpty) return; mask_region.X = masked.Left - mask_region.Left; mask_region.Y = masked.Top - mask_region.Top; mask_region.Width = masked.Width; mask_region.Height= masked.Height; if (mask_region.Width <= 0 || mask_region.Height <= 0) return; int x = masked.Left - self.Left; int y = masked.Top - self.Top; int dst = y * Info.Stride + x * pixel_size; int src = mask_region.Top * mask.Info.Stride + mask_region.Left; for (int row = 0; row < mask_region.Height; ++row) { int dst_pixel = dst+3; for (int col = 0; col < mask_region.Width; ++col) { m_output[dst_pixel] = mask.Data[src+col]; dst_pixel += pixel_size; } dst += Info.Stride; src += mask.Info.Stride; } }
IarImage CombineLayers(IarImage layers, IarArchive iarc) { layers.Info.Stride = (int)layers.Info.Width * 4; layers.Info.BPP = 32; var pixels = new byte[layers.Info.Stride * (int)layers.Info.Height]; var output = new IarImage (layers.Info, pixels); using (var mem = new MemoryStream (layers.Data)) using (var input = new BinaryReader (mem)) { int offset_x = 0, offset_y = 0; var dir = (List<Entry>)iarc.Dir; while (input.BaseStream.Position < input.BaseStream.Length) { int cmd = input.ReadByte(); switch (cmd) { case 0x21: offset_x += input.ReadInt16(); offset_y += input.ReadInt16(); break; case 0x00: case 0x20: { int index = input.ReadInt32(); if (index < 0 || index >= dir.Count) throw new InvalidFormatException ("Invalid image layer index"); var layer = new IarImage (iarc, dir[index]); layer.Info.OffsetX -= offset_x; layer.Info.OffsetY -= offset_y; if (0x20 == cmd) output.ApplyMask (layer); else output.Blend (layer); } break; default: Trace.WriteLine (string.Format ("Unknown layer type 0x{0:X2}", cmd), "IAR"); break; } } return output; } }
IarImage CombineImage(IarImage overlay, IarArchive iarc) { using (var mem = new MemoryStream (overlay.Data)) using (var input = new BinaryReader (mem)) { var dir = (List<Entry>)iarc.Dir; int base_index = input.ReadInt32(); if (base_index >= dir.Count) throw new InvalidFormatException ("Invalid base image index"); int diff_y = input.ReadInt32(); int diff_count = input.ReadInt32(); var overlay_info = overlay.Info; var base_image = new IarImage (iarc, dir[base_index]); byte[] output = base_image.Data; if (overlay_info.Height != base_image.Info.Height || overlay_info.Stride != base_image.Info.Stride) { int src_height = (int)Math.Min (overlay_info.Height, base_image.Info.Height); int src_stride = Math.Min (overlay_info.Stride, base_image.Info.Stride); byte[] src = base_image.Data; output = new byte[overlay_info.Height * overlay_info.Stride]; int dst_pos = 0; for (int y = 0; y < src_height; ++y) { Buffer.BlockCopy (src, y * base_image.Info.Stride, output, dst_pos, src_stride); dst_pos += overlay_info.Stride; } } int pixel_size = overlay_info.BPP / 8; int dst = diff_y * overlay_info.Stride; for (int i = 0; i < diff_count; ++i) { int chunk_count = input.ReadUInt16(); int x = 0; for (int j = 0; j < chunk_count; ++j) { int skip_count = pixel_size * input.ReadUInt16(); int copy_count = pixel_size * input.ReadUInt16(); x += skip_count; input.Read (output, dst+x, copy_count); x += copy_count; } dst += overlay_info.Stride; } return new IarImage (overlay_info, output, overlay.Palette); } }