Add tranparency to huge image C#
I have A0 format (600dpi) png (19860px x 28080px) that contains only black and white pixels (one bit per pixel file is about only 3MB). All I want is to save this file as png where white color pixels will be replaced by transparent color.
bitmap.MakeTransparent(color)
doesn't work because file is too big. Same issue with using ColorMap
Any ideas how to replace all those white pixels in reasonable time?
c# bitmap transparency system.drawing
|
show 6 more comments
I have A0 format (600dpi) png (19860px x 28080px) that contains only black and white pixels (one bit per pixel file is about only 3MB). All I want is to save this file as png where white color pixels will be replaced by transparent color.
bitmap.MakeTransparent(color)
doesn't work because file is too big. Same issue with using ColorMap
Any ideas how to replace all those white pixels in reasonable time?
c# bitmap transparency system.drawing
Have you tried usingSetPixel(...)
to achieve this?
– Martin Parkin
Nov 19 '18 at 12:54
Not yet. But SetPixel() is a bit slow. For 19860x28080 it will be last about 15min
– Bartek Chyży
Nov 19 '18 at 12:56
2
I'm sorry, you didn't say that it needed to be speedy in your question...
– Martin Parkin
Nov 19 '18 at 12:56
Right, my bad. I will have to process about 3000 images like that. :/
– Bartek Chyży
Nov 19 '18 at 12:58
You need to play with the colormap instead. ColorMap you technically change the table saying color ID "x" is now the RGB "y". On such large file it should be 4-5 seconds tops to change the whole thing. You may want to check my answer here
– Franck
Nov 19 '18 at 13:54
|
show 6 more comments
I have A0 format (600dpi) png (19860px x 28080px) that contains only black and white pixels (one bit per pixel file is about only 3MB). All I want is to save this file as png where white color pixels will be replaced by transparent color.
bitmap.MakeTransparent(color)
doesn't work because file is too big. Same issue with using ColorMap
Any ideas how to replace all those white pixels in reasonable time?
c# bitmap transparency system.drawing
I have A0 format (600dpi) png (19860px x 28080px) that contains only black and white pixels (one bit per pixel file is about only 3MB). All I want is to save this file as png where white color pixels will be replaced by transparent color.
bitmap.MakeTransparent(color)
doesn't work because file is too big. Same issue with using ColorMap
Any ideas how to replace all those white pixels in reasonable time?
c# bitmap transparency system.drawing
c# bitmap transparency system.drawing
edited Nov 19 '18 at 14:06
Bartek Chyży
asked Nov 19 '18 at 12:47
Bartek ChyżyBartek Chyży
8010
8010
Have you tried usingSetPixel(...)
to achieve this?
– Martin Parkin
Nov 19 '18 at 12:54
Not yet. But SetPixel() is a bit slow. For 19860x28080 it will be last about 15min
– Bartek Chyży
Nov 19 '18 at 12:56
2
I'm sorry, you didn't say that it needed to be speedy in your question...
– Martin Parkin
Nov 19 '18 at 12:56
Right, my bad. I will have to process about 3000 images like that. :/
– Bartek Chyży
Nov 19 '18 at 12:58
You need to play with the colormap instead. ColorMap you technically change the table saying color ID "x" is now the RGB "y". On such large file it should be 4-5 seconds tops to change the whole thing. You may want to check my answer here
– Franck
Nov 19 '18 at 13:54
|
show 6 more comments
Have you tried usingSetPixel(...)
to achieve this?
– Martin Parkin
Nov 19 '18 at 12:54
Not yet. But SetPixel() is a bit slow. For 19860x28080 it will be last about 15min
– Bartek Chyży
Nov 19 '18 at 12:56
2
I'm sorry, you didn't say that it needed to be speedy in your question...
– Martin Parkin
Nov 19 '18 at 12:56
Right, my bad. I will have to process about 3000 images like that. :/
– Bartek Chyży
Nov 19 '18 at 12:58
You need to play with the colormap instead. ColorMap you technically change the table saying color ID "x" is now the RGB "y". On such large file it should be 4-5 seconds tops to change the whole thing. You may want to check my answer here
– Franck
Nov 19 '18 at 13:54
Have you tried using
SetPixel(...)
to achieve this?– Martin Parkin
Nov 19 '18 at 12:54
Have you tried using
SetPixel(...)
to achieve this?– Martin Parkin
Nov 19 '18 at 12:54
Not yet. But SetPixel() is a bit slow. For 19860x28080 it will be last about 15min
– Bartek Chyży
Nov 19 '18 at 12:56
Not yet. But SetPixel() is a bit slow. For 19860x28080 it will be last about 15min
– Bartek Chyży
Nov 19 '18 at 12:56
2
2
I'm sorry, you didn't say that it needed to be speedy in your question...
– Martin Parkin
Nov 19 '18 at 12:56
I'm sorry, you didn't say that it needed to be speedy in your question...
– Martin Parkin
Nov 19 '18 at 12:56
Right, my bad. I will have to process about 3000 images like that. :/
– Bartek Chyży
Nov 19 '18 at 12:58
Right, my bad. I will have to process about 3000 images like that. :/
– Bartek Chyży
Nov 19 '18 at 12:58
You need to play with the colormap instead. ColorMap you technically change the table saying color ID "x" is now the RGB "y". On such large file it should be 4-5 seconds tops to change the whole thing. You may want to check my answer here
– Franck
Nov 19 '18 at 13:54
You need to play with the colormap instead. ColorMap you technically change the table saying color ID "x" is now the RGB "y". On such large file it should be 4-5 seconds tops to change the whole thing. You may want to check my answer here
– Franck
Nov 19 '18 at 13:54
|
show 6 more comments
2 Answers
2
active
oldest
votes
I'm not an expert on PNG files, but I read the documentation. I believe if you have a GRAEY SCALE file with only one byte data then all you have to do is add a transparency header before the first IDATA chunk. The transparency header contains two bytes which is a color scale between 0 and (2^bitdepth - 1)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
namespace PNG_Tool
{
class Program
{
const string READ_FILENAME = @"c:tempuntitled.png";
const string WRITE_FILENAME = @"c:tempuntitled1.png";
static void Main(string args)
{
PNG png = new PNG(READ_FILENAME, WRITE_FILENAME);
}
}
class PNG
{
byte header;
byte ident = { 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A};
byte TNS = { 0x74, 0x52, 0x4E, 0x53 }; //"tRNS"
public PNG(string inFilename, string outFilename)
{
Stream inStream = File.OpenRead(inFilename);
BinaryReader reader = new BinaryReader(inStream);
Stream outStream = File.Open(outFilename, FileMode.Create);
BinaryWriter writer = new BinaryWriter(outStream);
Boolean foundIDAT = false;
header = reader.ReadBytes(8);
if ((header.Length != ident.Length) || !(header.Select((x,i) => (x == ident[i])).All(x => x)))
{
Console.WriteLine("File is not PNG");
return;
}
writer.Write(header);
while (inStream.Position < inStream.Length)
{
byte byteLength = reader.ReadBytes(4);
if (byteLength.Length < 4)
{
Console.WriteLine("Unexpected End Of File");
return;
}
UInt32 length = (UInt32)((byteLength[0] << 24) | (byteLength[1] << 16) | (byteLength[2] << 8) | byteLength[3]);
byte chunkType = reader.ReadBytes(4);
if (chunkType.Length < 4)
{
Console.WriteLine("Unexpected End Of File");
return;
}
string chunkName = Encoding.ASCII.GetString(chunkType);
byte data = reader.ReadBytes((int)length);
if (data.Length < length)
{
Console.WriteLine("Unexpected End Of File");
return;
}
byte CRC = reader.ReadBytes(4);
if (CRC.Length < 4)
{
Console.WriteLine("Unexpected End Of File");
return;
}
uint crc = GetCRC(chunkType, data);
UInt32 ExpectedCRC = (UInt32)((CRC[0] << 24) | (CRC[1] << 16) | (CRC[2] << 8) | CRC[3]);
if (crc != ExpectedCRC)
{
Console.WriteLine("Bad CRC");
}
switch (chunkName)
{
case "IHDR" :
writer.Write(byteLength);
writer.Write(chunkType);
writer.Write(data);
Header chunkHeader = new Header(data);
chunkHeader.PrintImageHeader();
break;
case "IDAT" :
if (!foundIDAT)
{
//add transparency header before first IDAT header
byte tnsHeader = CreateTransparencyHeader();
writer.Write(tnsHeader);
foundIDAT = true;
}
writer.Write(byteLength);
writer.Write(chunkType);
writer.Write(data);
break;
default :
writer.Write(byteLength);
writer.Write(chunkType);
writer.Write(data);
break;
}
writer.Write(CRC);
}
reader.Close();
writer.Flush();
writer.Close();
}
public byte CreateTransparencyHeader()
{
byte white = { 0, 0 };
List<byte> header = new List<byte>();
byte length = { 0, 0, 0, 2 }; //length is just two bytes
header.AddRange(length);
header.AddRange(TNS);
header.AddRange(white);
UInt32 crc = GetCRC(TNS, white);
byte crcBytes = { (byte)((crc >> 24) & 0xFF), (byte)((crc >> 16) & 0xFF), (byte)((crc >> 8) & 0xFF), (byte)(crc & 0xFF) };
header.AddRange(crcBytes);
return header.ToArray();
}
public uint GetCRC(byte type, byte bytes)
{
uint crc = 0xffffffff; /* CRC value is 32bit */
//crc = CRC32(byteLength, crc);
crc = CRC32(type, crc);
crc = CRC32(bytes, crc);
crc = Reflect(crc, 32);
crc ^= 0xFFFFFFFF;
return crc;
}
public uint CRC32(byte bytes, uint crc)
{
const uint polynomial = 0x04C11DB7; /* divisor is 32bit */
foreach (byte b in bytes)
{
crc ^= (uint)(Reflect(b, 8) << 24); /* move byte into MSB of 32bit CRC */
for (int i = 0; i < 8; i++)
{
if ((crc & 0x80000000) != 0) /* test for MSB = bit 31 */
{
crc = (uint)((crc << 1) ^ polynomial);
}
else
{
crc <<= 1;
}
}
}
return crc;
}
static public UInt32 Reflect(UInt32 data, int size)
{
UInt32 output = 0;
for (int i = 0; i < size; i++)
{
UInt32 lsb = data & 0x01;
output = (UInt32)((output << 1) | lsb);
data >>= 1;
}
return output;
}
}
public class Header
{
public UInt32 width { get; set; }
public UInt32 height { get; set; }
byte widthBytes { get; set; }
byte heightBytes { get; set; }
public byte depth { get; set; }
public byte colourType { get; set; }
public byte compressionMethod { get; set; }
public byte filterMethod { get; set; }
public byte interlaceMethod { get; set; }
public Header(byte bytes)
{
UInt32 width = (UInt32)((bytes[0] << 24) | (bytes[1] << 16) | (bytes[2] << 8) | bytes[3]);
UInt32 height = (UInt32)((bytes[4] << 24) | (bytes[5] << 16) | (bytes[6] << 8) | bytes[7]);
widthBytes = new byte[4];
Array.Copy(bytes, widthBytes, 4);
heightBytes = new byte[4];
Array.Copy(bytes, 4, heightBytes, 0, 4);
depth = bytes[8];
colourType = bytes[9];
compressionMethod = bytes[10];
filterMethod = bytes[11];
interlaceMethod = bytes[12];
}
public void PrintImageHeader()
{
Console.WriteLine("Width = '{0}', Height = '{1}', Bit Depth = '{2}', Colour Type = '{3}', Compression Method = '{4}', Filter Medthod = '{5}', Interlace Method = '{6}'",
width.ToString(),
height.ToString(),
depth.ToString(),
((COLOUR_TYPE)colourType).ToString(),
compressionMethod.ToString(),
filterMethod.ToString(),
interlaceMethod.ToString()
);
}
public byte GetHeader()
{
List<byte> header = new List<byte>();
header.AddRange(widthBytes);
header.AddRange(heightBytes);
header.Add(depth);
header.Add(colourType);
header.Add(compressionMethod);
header.Add(filterMethod);
header.Add(interlaceMethod);
return header.ToArray();
}
}
public enum COLOUR_TYPE
{
GRAY_SCALE = 0,
TRUE_COLOUR = 2,
INDEXED_COLOUR = 3,
GREY_SCALE_ALPHA = 4,
TRUE_COLOUR_ALPHA = 6
}
}
Sounds like a good question. Doesn't sound like an answer, though.
– TaW
Nov 19 '18 at 19:04
I wasn't sure what the colour mode was so I couldn't complete the code initially. Reading the comments it appears the OP says it file is one byte Grey Scale so I assumed it was colour mode 0. Reading the PNG documentation is looks like adding a transparency chunk before 1st data chunk will solve issue.
– jdweng
Nov 20 '18 at 12:50
Valid answer, but overcomplicated. You can just extract the palette and surgically insert the tRNS chunk, and be done with it. No need for full parsing of the png file at all.
– Nyerguds
Nov 20 '18 at 13:41
Note that technically the tRNS chunk doesn't have to be the full palette size; any unspecified alpha values default to opaque.
– Nyerguds
Nov 20 '18 at 14:03
Does a GreyScale image contain a palette chunk? I only have full color images so I do not know the chunks in a GreyScale image. I only made the tRNS palette two bytes and made the color value 0. Nothing says in the documentation says the Transparent chunk should be the same size as the palette. There is nothing complicated with my code. It is the simplest I could make it and do everything necessary to add the transparency chunk. The only thing extra I added was to display the Header so we can tell the colorour mode.
– jdweng
Nov 20 '18 at 14:10
|
show 5 more comments
You don't need System.Drawing
for this operation at all.
See, one-bit-per-pixel black-and-white images in PNG format will be either in grayscale format, or paletted.
PNG is made up of chunks which have the following format:
- Four bytes of internal chunk length (big-endian)
- Four ASCII characters of chunk identifier
- The chunk contents (with the length specified in the first part)
- A four-byte CRC hash as consistency check.
Now here's the interesting part: PNG has the rather obscure feature that it supports a tRNS
chunk to be added that sets the alpha for grayscale and paletted format. For grayscale this chunk contains a two-byte value that indicates which grayscale value should be made transparent (which I assume for one bit per pixel should be '1', since that's white), and for paletted format this contains the alpha for each of its palette colours (though it doesn't have to be as long as the palette; any indices not included default to opaque). And since PNG has no overall indexing of its chunks, you can literally just add that in and be done with it.
The code for reading and writing PNG chunks was posted in earlier answers here:
Reading chunks
Writing chunks
You will need to read the IHDR
chunk and check the colour type in the header to see which type of transparency chunk you need to add. An overview of the header format and all colour type possibilities can be found here. Basically, colour type 0 is grayscale, and colour type 3 is paletted, so your image should be one of these two.
For palette transparency, the tRNS
chunk should be added right behind the PLTE
chunk. For grayscale transparency, I believe it should be just before the first IDAT
chunk.
If it's paletted, you do need to check if white is the first or second colour in the palette, so you can set the correct one to transparent.
So once you got that, make a new byte array that's as large as your image plus the added chunk (12 bytes of chunk header and footer and probably 2 bytes of data inside it), and then copy the first part of the file up to the point where the segment should be added into it, then your new segment, and then the rest of the file. Save the bytes array to a file, and you're done.
the link you posted is working from bitmaps and not a png file. Also you have to be careful of the chunk order. The posted code is randomly checking for a chunk type and not sequentially going through the chunks.
– jdweng
Nov 20 '18 at 21:13
No, the link I posted returns a .NetBitmap
object (since its purpose is to load the palette transparency correctly in .Net), but it takes bytes as input. As for the order of the chunks, it does in fact not matter at all for the search procedure. It matters in that the tRNS chunk needs to be right behind the PLTE one, but I specifically mentioned that.
– Nyerguds
Nov 21 '18 at 9:08
add a comment |
Your Answer
StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");
StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "1"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});
function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53374979%2fadd-tranparency-to-huge-image-c-sharp%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
2 Answers
2
active
oldest
votes
2 Answers
2
active
oldest
votes
active
oldest
votes
active
oldest
votes
I'm not an expert on PNG files, but I read the documentation. I believe if you have a GRAEY SCALE file with only one byte data then all you have to do is add a transparency header before the first IDATA chunk. The transparency header contains two bytes which is a color scale between 0 and (2^bitdepth - 1)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
namespace PNG_Tool
{
class Program
{
const string READ_FILENAME = @"c:tempuntitled.png";
const string WRITE_FILENAME = @"c:tempuntitled1.png";
static void Main(string args)
{
PNG png = new PNG(READ_FILENAME, WRITE_FILENAME);
}
}
class PNG
{
byte header;
byte ident = { 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A};
byte TNS = { 0x74, 0x52, 0x4E, 0x53 }; //"tRNS"
public PNG(string inFilename, string outFilename)
{
Stream inStream = File.OpenRead(inFilename);
BinaryReader reader = new BinaryReader(inStream);
Stream outStream = File.Open(outFilename, FileMode.Create);
BinaryWriter writer = new BinaryWriter(outStream);
Boolean foundIDAT = false;
header = reader.ReadBytes(8);
if ((header.Length != ident.Length) || !(header.Select((x,i) => (x == ident[i])).All(x => x)))
{
Console.WriteLine("File is not PNG");
return;
}
writer.Write(header);
while (inStream.Position < inStream.Length)
{
byte byteLength = reader.ReadBytes(4);
if (byteLength.Length < 4)
{
Console.WriteLine("Unexpected End Of File");
return;
}
UInt32 length = (UInt32)((byteLength[0] << 24) | (byteLength[1] << 16) | (byteLength[2] << 8) | byteLength[3]);
byte chunkType = reader.ReadBytes(4);
if (chunkType.Length < 4)
{
Console.WriteLine("Unexpected End Of File");
return;
}
string chunkName = Encoding.ASCII.GetString(chunkType);
byte data = reader.ReadBytes((int)length);
if (data.Length < length)
{
Console.WriteLine("Unexpected End Of File");
return;
}
byte CRC = reader.ReadBytes(4);
if (CRC.Length < 4)
{
Console.WriteLine("Unexpected End Of File");
return;
}
uint crc = GetCRC(chunkType, data);
UInt32 ExpectedCRC = (UInt32)((CRC[0] << 24) | (CRC[1] << 16) | (CRC[2] << 8) | CRC[3]);
if (crc != ExpectedCRC)
{
Console.WriteLine("Bad CRC");
}
switch (chunkName)
{
case "IHDR" :
writer.Write(byteLength);
writer.Write(chunkType);
writer.Write(data);
Header chunkHeader = new Header(data);
chunkHeader.PrintImageHeader();
break;
case "IDAT" :
if (!foundIDAT)
{
//add transparency header before first IDAT header
byte tnsHeader = CreateTransparencyHeader();
writer.Write(tnsHeader);
foundIDAT = true;
}
writer.Write(byteLength);
writer.Write(chunkType);
writer.Write(data);
break;
default :
writer.Write(byteLength);
writer.Write(chunkType);
writer.Write(data);
break;
}
writer.Write(CRC);
}
reader.Close();
writer.Flush();
writer.Close();
}
public byte CreateTransparencyHeader()
{
byte white = { 0, 0 };
List<byte> header = new List<byte>();
byte length = { 0, 0, 0, 2 }; //length is just two bytes
header.AddRange(length);
header.AddRange(TNS);
header.AddRange(white);
UInt32 crc = GetCRC(TNS, white);
byte crcBytes = { (byte)((crc >> 24) & 0xFF), (byte)((crc >> 16) & 0xFF), (byte)((crc >> 8) & 0xFF), (byte)(crc & 0xFF) };
header.AddRange(crcBytes);
return header.ToArray();
}
public uint GetCRC(byte type, byte bytes)
{
uint crc = 0xffffffff; /* CRC value is 32bit */
//crc = CRC32(byteLength, crc);
crc = CRC32(type, crc);
crc = CRC32(bytes, crc);
crc = Reflect(crc, 32);
crc ^= 0xFFFFFFFF;
return crc;
}
public uint CRC32(byte bytes, uint crc)
{
const uint polynomial = 0x04C11DB7; /* divisor is 32bit */
foreach (byte b in bytes)
{
crc ^= (uint)(Reflect(b, 8) << 24); /* move byte into MSB of 32bit CRC */
for (int i = 0; i < 8; i++)
{
if ((crc & 0x80000000) != 0) /* test for MSB = bit 31 */
{
crc = (uint)((crc << 1) ^ polynomial);
}
else
{
crc <<= 1;
}
}
}
return crc;
}
static public UInt32 Reflect(UInt32 data, int size)
{
UInt32 output = 0;
for (int i = 0; i < size; i++)
{
UInt32 lsb = data & 0x01;
output = (UInt32)((output << 1) | lsb);
data >>= 1;
}
return output;
}
}
public class Header
{
public UInt32 width { get; set; }
public UInt32 height { get; set; }
byte widthBytes { get; set; }
byte heightBytes { get; set; }
public byte depth { get; set; }
public byte colourType { get; set; }
public byte compressionMethod { get; set; }
public byte filterMethod { get; set; }
public byte interlaceMethod { get; set; }
public Header(byte bytes)
{
UInt32 width = (UInt32)((bytes[0] << 24) | (bytes[1] << 16) | (bytes[2] << 8) | bytes[3]);
UInt32 height = (UInt32)((bytes[4] << 24) | (bytes[5] << 16) | (bytes[6] << 8) | bytes[7]);
widthBytes = new byte[4];
Array.Copy(bytes, widthBytes, 4);
heightBytes = new byte[4];
Array.Copy(bytes, 4, heightBytes, 0, 4);
depth = bytes[8];
colourType = bytes[9];
compressionMethod = bytes[10];
filterMethod = bytes[11];
interlaceMethod = bytes[12];
}
public void PrintImageHeader()
{
Console.WriteLine("Width = '{0}', Height = '{1}', Bit Depth = '{2}', Colour Type = '{3}', Compression Method = '{4}', Filter Medthod = '{5}', Interlace Method = '{6}'",
width.ToString(),
height.ToString(),
depth.ToString(),
((COLOUR_TYPE)colourType).ToString(),
compressionMethod.ToString(),
filterMethod.ToString(),
interlaceMethod.ToString()
);
}
public byte GetHeader()
{
List<byte> header = new List<byte>();
header.AddRange(widthBytes);
header.AddRange(heightBytes);
header.Add(depth);
header.Add(colourType);
header.Add(compressionMethod);
header.Add(filterMethod);
header.Add(interlaceMethod);
return header.ToArray();
}
}
public enum COLOUR_TYPE
{
GRAY_SCALE = 0,
TRUE_COLOUR = 2,
INDEXED_COLOUR = 3,
GREY_SCALE_ALPHA = 4,
TRUE_COLOUR_ALPHA = 6
}
}
Sounds like a good question. Doesn't sound like an answer, though.
– TaW
Nov 19 '18 at 19:04
I wasn't sure what the colour mode was so I couldn't complete the code initially. Reading the comments it appears the OP says it file is one byte Grey Scale so I assumed it was colour mode 0. Reading the PNG documentation is looks like adding a transparency chunk before 1st data chunk will solve issue.
– jdweng
Nov 20 '18 at 12:50
Valid answer, but overcomplicated. You can just extract the palette and surgically insert the tRNS chunk, and be done with it. No need for full parsing of the png file at all.
– Nyerguds
Nov 20 '18 at 13:41
Note that technically the tRNS chunk doesn't have to be the full palette size; any unspecified alpha values default to opaque.
– Nyerguds
Nov 20 '18 at 14:03
Does a GreyScale image contain a palette chunk? I only have full color images so I do not know the chunks in a GreyScale image. I only made the tRNS palette two bytes and made the color value 0. Nothing says in the documentation says the Transparent chunk should be the same size as the palette. There is nothing complicated with my code. It is the simplest I could make it and do everything necessary to add the transparency chunk. The only thing extra I added was to display the Header so we can tell the colorour mode.
– jdweng
Nov 20 '18 at 14:10
|
show 5 more comments
I'm not an expert on PNG files, but I read the documentation. I believe if you have a GRAEY SCALE file with only one byte data then all you have to do is add a transparency header before the first IDATA chunk. The transparency header contains two bytes which is a color scale between 0 and (2^bitdepth - 1)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
namespace PNG_Tool
{
class Program
{
const string READ_FILENAME = @"c:tempuntitled.png";
const string WRITE_FILENAME = @"c:tempuntitled1.png";
static void Main(string args)
{
PNG png = new PNG(READ_FILENAME, WRITE_FILENAME);
}
}
class PNG
{
byte header;
byte ident = { 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A};
byte TNS = { 0x74, 0x52, 0x4E, 0x53 }; //"tRNS"
public PNG(string inFilename, string outFilename)
{
Stream inStream = File.OpenRead(inFilename);
BinaryReader reader = new BinaryReader(inStream);
Stream outStream = File.Open(outFilename, FileMode.Create);
BinaryWriter writer = new BinaryWriter(outStream);
Boolean foundIDAT = false;
header = reader.ReadBytes(8);
if ((header.Length != ident.Length) || !(header.Select((x,i) => (x == ident[i])).All(x => x)))
{
Console.WriteLine("File is not PNG");
return;
}
writer.Write(header);
while (inStream.Position < inStream.Length)
{
byte byteLength = reader.ReadBytes(4);
if (byteLength.Length < 4)
{
Console.WriteLine("Unexpected End Of File");
return;
}
UInt32 length = (UInt32)((byteLength[0] << 24) | (byteLength[1] << 16) | (byteLength[2] << 8) | byteLength[3]);
byte chunkType = reader.ReadBytes(4);
if (chunkType.Length < 4)
{
Console.WriteLine("Unexpected End Of File");
return;
}
string chunkName = Encoding.ASCII.GetString(chunkType);
byte data = reader.ReadBytes((int)length);
if (data.Length < length)
{
Console.WriteLine("Unexpected End Of File");
return;
}
byte CRC = reader.ReadBytes(4);
if (CRC.Length < 4)
{
Console.WriteLine("Unexpected End Of File");
return;
}
uint crc = GetCRC(chunkType, data);
UInt32 ExpectedCRC = (UInt32)((CRC[0] << 24) | (CRC[1] << 16) | (CRC[2] << 8) | CRC[3]);
if (crc != ExpectedCRC)
{
Console.WriteLine("Bad CRC");
}
switch (chunkName)
{
case "IHDR" :
writer.Write(byteLength);
writer.Write(chunkType);
writer.Write(data);
Header chunkHeader = new Header(data);
chunkHeader.PrintImageHeader();
break;
case "IDAT" :
if (!foundIDAT)
{
//add transparency header before first IDAT header
byte tnsHeader = CreateTransparencyHeader();
writer.Write(tnsHeader);
foundIDAT = true;
}
writer.Write(byteLength);
writer.Write(chunkType);
writer.Write(data);
break;
default :
writer.Write(byteLength);
writer.Write(chunkType);
writer.Write(data);
break;
}
writer.Write(CRC);
}
reader.Close();
writer.Flush();
writer.Close();
}
public byte CreateTransparencyHeader()
{
byte white = { 0, 0 };
List<byte> header = new List<byte>();
byte length = { 0, 0, 0, 2 }; //length is just two bytes
header.AddRange(length);
header.AddRange(TNS);
header.AddRange(white);
UInt32 crc = GetCRC(TNS, white);
byte crcBytes = { (byte)((crc >> 24) & 0xFF), (byte)((crc >> 16) & 0xFF), (byte)((crc >> 8) & 0xFF), (byte)(crc & 0xFF) };
header.AddRange(crcBytes);
return header.ToArray();
}
public uint GetCRC(byte type, byte bytes)
{
uint crc = 0xffffffff; /* CRC value is 32bit */
//crc = CRC32(byteLength, crc);
crc = CRC32(type, crc);
crc = CRC32(bytes, crc);
crc = Reflect(crc, 32);
crc ^= 0xFFFFFFFF;
return crc;
}
public uint CRC32(byte bytes, uint crc)
{
const uint polynomial = 0x04C11DB7; /* divisor is 32bit */
foreach (byte b in bytes)
{
crc ^= (uint)(Reflect(b, 8) << 24); /* move byte into MSB of 32bit CRC */
for (int i = 0; i < 8; i++)
{
if ((crc & 0x80000000) != 0) /* test for MSB = bit 31 */
{
crc = (uint)((crc << 1) ^ polynomial);
}
else
{
crc <<= 1;
}
}
}
return crc;
}
static public UInt32 Reflect(UInt32 data, int size)
{
UInt32 output = 0;
for (int i = 0; i < size; i++)
{
UInt32 lsb = data & 0x01;
output = (UInt32)((output << 1) | lsb);
data >>= 1;
}
return output;
}
}
public class Header
{
public UInt32 width { get; set; }
public UInt32 height { get; set; }
byte widthBytes { get; set; }
byte heightBytes { get; set; }
public byte depth { get; set; }
public byte colourType { get; set; }
public byte compressionMethod { get; set; }
public byte filterMethod { get; set; }
public byte interlaceMethod { get; set; }
public Header(byte bytes)
{
UInt32 width = (UInt32)((bytes[0] << 24) | (bytes[1] << 16) | (bytes[2] << 8) | bytes[3]);
UInt32 height = (UInt32)((bytes[4] << 24) | (bytes[5] << 16) | (bytes[6] << 8) | bytes[7]);
widthBytes = new byte[4];
Array.Copy(bytes, widthBytes, 4);
heightBytes = new byte[4];
Array.Copy(bytes, 4, heightBytes, 0, 4);
depth = bytes[8];
colourType = bytes[9];
compressionMethod = bytes[10];
filterMethod = bytes[11];
interlaceMethod = bytes[12];
}
public void PrintImageHeader()
{
Console.WriteLine("Width = '{0}', Height = '{1}', Bit Depth = '{2}', Colour Type = '{3}', Compression Method = '{4}', Filter Medthod = '{5}', Interlace Method = '{6}'",
width.ToString(),
height.ToString(),
depth.ToString(),
((COLOUR_TYPE)colourType).ToString(),
compressionMethod.ToString(),
filterMethod.ToString(),
interlaceMethod.ToString()
);
}
public byte GetHeader()
{
List<byte> header = new List<byte>();
header.AddRange(widthBytes);
header.AddRange(heightBytes);
header.Add(depth);
header.Add(colourType);
header.Add(compressionMethod);
header.Add(filterMethod);
header.Add(interlaceMethod);
return header.ToArray();
}
}
public enum COLOUR_TYPE
{
GRAY_SCALE = 0,
TRUE_COLOUR = 2,
INDEXED_COLOUR = 3,
GREY_SCALE_ALPHA = 4,
TRUE_COLOUR_ALPHA = 6
}
}
Sounds like a good question. Doesn't sound like an answer, though.
– TaW
Nov 19 '18 at 19:04
I wasn't sure what the colour mode was so I couldn't complete the code initially. Reading the comments it appears the OP says it file is one byte Grey Scale so I assumed it was colour mode 0. Reading the PNG documentation is looks like adding a transparency chunk before 1st data chunk will solve issue.
– jdweng
Nov 20 '18 at 12:50
Valid answer, but overcomplicated. You can just extract the palette and surgically insert the tRNS chunk, and be done with it. No need for full parsing of the png file at all.
– Nyerguds
Nov 20 '18 at 13:41
Note that technically the tRNS chunk doesn't have to be the full palette size; any unspecified alpha values default to opaque.
– Nyerguds
Nov 20 '18 at 14:03
Does a GreyScale image contain a palette chunk? I only have full color images so I do not know the chunks in a GreyScale image. I only made the tRNS palette two bytes and made the color value 0. Nothing says in the documentation says the Transparent chunk should be the same size as the palette. There is nothing complicated with my code. It is the simplest I could make it and do everything necessary to add the transparency chunk. The only thing extra I added was to display the Header so we can tell the colorour mode.
– jdweng
Nov 20 '18 at 14:10
|
show 5 more comments
I'm not an expert on PNG files, but I read the documentation. I believe if you have a GRAEY SCALE file with only one byte data then all you have to do is add a transparency header before the first IDATA chunk. The transparency header contains two bytes which is a color scale between 0 and (2^bitdepth - 1)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
namespace PNG_Tool
{
class Program
{
const string READ_FILENAME = @"c:tempuntitled.png";
const string WRITE_FILENAME = @"c:tempuntitled1.png";
static void Main(string args)
{
PNG png = new PNG(READ_FILENAME, WRITE_FILENAME);
}
}
class PNG
{
byte header;
byte ident = { 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A};
byte TNS = { 0x74, 0x52, 0x4E, 0x53 }; //"tRNS"
public PNG(string inFilename, string outFilename)
{
Stream inStream = File.OpenRead(inFilename);
BinaryReader reader = new BinaryReader(inStream);
Stream outStream = File.Open(outFilename, FileMode.Create);
BinaryWriter writer = new BinaryWriter(outStream);
Boolean foundIDAT = false;
header = reader.ReadBytes(8);
if ((header.Length != ident.Length) || !(header.Select((x,i) => (x == ident[i])).All(x => x)))
{
Console.WriteLine("File is not PNG");
return;
}
writer.Write(header);
while (inStream.Position < inStream.Length)
{
byte byteLength = reader.ReadBytes(4);
if (byteLength.Length < 4)
{
Console.WriteLine("Unexpected End Of File");
return;
}
UInt32 length = (UInt32)((byteLength[0] << 24) | (byteLength[1] << 16) | (byteLength[2] << 8) | byteLength[3]);
byte chunkType = reader.ReadBytes(4);
if (chunkType.Length < 4)
{
Console.WriteLine("Unexpected End Of File");
return;
}
string chunkName = Encoding.ASCII.GetString(chunkType);
byte data = reader.ReadBytes((int)length);
if (data.Length < length)
{
Console.WriteLine("Unexpected End Of File");
return;
}
byte CRC = reader.ReadBytes(4);
if (CRC.Length < 4)
{
Console.WriteLine("Unexpected End Of File");
return;
}
uint crc = GetCRC(chunkType, data);
UInt32 ExpectedCRC = (UInt32)((CRC[0] << 24) | (CRC[1] << 16) | (CRC[2] << 8) | CRC[3]);
if (crc != ExpectedCRC)
{
Console.WriteLine("Bad CRC");
}
switch (chunkName)
{
case "IHDR" :
writer.Write(byteLength);
writer.Write(chunkType);
writer.Write(data);
Header chunkHeader = new Header(data);
chunkHeader.PrintImageHeader();
break;
case "IDAT" :
if (!foundIDAT)
{
//add transparency header before first IDAT header
byte tnsHeader = CreateTransparencyHeader();
writer.Write(tnsHeader);
foundIDAT = true;
}
writer.Write(byteLength);
writer.Write(chunkType);
writer.Write(data);
break;
default :
writer.Write(byteLength);
writer.Write(chunkType);
writer.Write(data);
break;
}
writer.Write(CRC);
}
reader.Close();
writer.Flush();
writer.Close();
}
public byte CreateTransparencyHeader()
{
byte white = { 0, 0 };
List<byte> header = new List<byte>();
byte length = { 0, 0, 0, 2 }; //length is just two bytes
header.AddRange(length);
header.AddRange(TNS);
header.AddRange(white);
UInt32 crc = GetCRC(TNS, white);
byte crcBytes = { (byte)((crc >> 24) & 0xFF), (byte)((crc >> 16) & 0xFF), (byte)((crc >> 8) & 0xFF), (byte)(crc & 0xFF) };
header.AddRange(crcBytes);
return header.ToArray();
}
public uint GetCRC(byte type, byte bytes)
{
uint crc = 0xffffffff; /* CRC value is 32bit */
//crc = CRC32(byteLength, crc);
crc = CRC32(type, crc);
crc = CRC32(bytes, crc);
crc = Reflect(crc, 32);
crc ^= 0xFFFFFFFF;
return crc;
}
public uint CRC32(byte bytes, uint crc)
{
const uint polynomial = 0x04C11DB7; /* divisor is 32bit */
foreach (byte b in bytes)
{
crc ^= (uint)(Reflect(b, 8) << 24); /* move byte into MSB of 32bit CRC */
for (int i = 0; i < 8; i++)
{
if ((crc & 0x80000000) != 0) /* test for MSB = bit 31 */
{
crc = (uint)((crc << 1) ^ polynomial);
}
else
{
crc <<= 1;
}
}
}
return crc;
}
static public UInt32 Reflect(UInt32 data, int size)
{
UInt32 output = 0;
for (int i = 0; i < size; i++)
{
UInt32 lsb = data & 0x01;
output = (UInt32)((output << 1) | lsb);
data >>= 1;
}
return output;
}
}
public class Header
{
public UInt32 width { get; set; }
public UInt32 height { get; set; }
byte widthBytes { get; set; }
byte heightBytes { get; set; }
public byte depth { get; set; }
public byte colourType { get; set; }
public byte compressionMethod { get; set; }
public byte filterMethod { get; set; }
public byte interlaceMethod { get; set; }
public Header(byte bytes)
{
UInt32 width = (UInt32)((bytes[0] << 24) | (bytes[1] << 16) | (bytes[2] << 8) | bytes[3]);
UInt32 height = (UInt32)((bytes[4] << 24) | (bytes[5] << 16) | (bytes[6] << 8) | bytes[7]);
widthBytes = new byte[4];
Array.Copy(bytes, widthBytes, 4);
heightBytes = new byte[4];
Array.Copy(bytes, 4, heightBytes, 0, 4);
depth = bytes[8];
colourType = bytes[9];
compressionMethod = bytes[10];
filterMethod = bytes[11];
interlaceMethod = bytes[12];
}
public void PrintImageHeader()
{
Console.WriteLine("Width = '{0}', Height = '{1}', Bit Depth = '{2}', Colour Type = '{3}', Compression Method = '{4}', Filter Medthod = '{5}', Interlace Method = '{6}'",
width.ToString(),
height.ToString(),
depth.ToString(),
((COLOUR_TYPE)colourType).ToString(),
compressionMethod.ToString(),
filterMethod.ToString(),
interlaceMethod.ToString()
);
}
public byte GetHeader()
{
List<byte> header = new List<byte>();
header.AddRange(widthBytes);
header.AddRange(heightBytes);
header.Add(depth);
header.Add(colourType);
header.Add(compressionMethod);
header.Add(filterMethod);
header.Add(interlaceMethod);
return header.ToArray();
}
}
public enum COLOUR_TYPE
{
GRAY_SCALE = 0,
TRUE_COLOUR = 2,
INDEXED_COLOUR = 3,
GREY_SCALE_ALPHA = 4,
TRUE_COLOUR_ALPHA = 6
}
}
I'm not an expert on PNG files, but I read the documentation. I believe if you have a GRAEY SCALE file with only one byte data then all you have to do is add a transparency header before the first IDATA chunk. The transparency header contains two bytes which is a color scale between 0 and (2^bitdepth - 1)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
namespace PNG_Tool
{
class Program
{
const string READ_FILENAME = @"c:tempuntitled.png";
const string WRITE_FILENAME = @"c:tempuntitled1.png";
static void Main(string args)
{
PNG png = new PNG(READ_FILENAME, WRITE_FILENAME);
}
}
class PNG
{
byte header;
byte ident = { 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A};
byte TNS = { 0x74, 0x52, 0x4E, 0x53 }; //"tRNS"
public PNG(string inFilename, string outFilename)
{
Stream inStream = File.OpenRead(inFilename);
BinaryReader reader = new BinaryReader(inStream);
Stream outStream = File.Open(outFilename, FileMode.Create);
BinaryWriter writer = new BinaryWriter(outStream);
Boolean foundIDAT = false;
header = reader.ReadBytes(8);
if ((header.Length != ident.Length) || !(header.Select((x,i) => (x == ident[i])).All(x => x)))
{
Console.WriteLine("File is not PNG");
return;
}
writer.Write(header);
while (inStream.Position < inStream.Length)
{
byte byteLength = reader.ReadBytes(4);
if (byteLength.Length < 4)
{
Console.WriteLine("Unexpected End Of File");
return;
}
UInt32 length = (UInt32)((byteLength[0] << 24) | (byteLength[1] << 16) | (byteLength[2] << 8) | byteLength[3]);
byte chunkType = reader.ReadBytes(4);
if (chunkType.Length < 4)
{
Console.WriteLine("Unexpected End Of File");
return;
}
string chunkName = Encoding.ASCII.GetString(chunkType);
byte data = reader.ReadBytes((int)length);
if (data.Length < length)
{
Console.WriteLine("Unexpected End Of File");
return;
}
byte CRC = reader.ReadBytes(4);
if (CRC.Length < 4)
{
Console.WriteLine("Unexpected End Of File");
return;
}
uint crc = GetCRC(chunkType, data);
UInt32 ExpectedCRC = (UInt32)((CRC[0] << 24) | (CRC[1] << 16) | (CRC[2] << 8) | CRC[3]);
if (crc != ExpectedCRC)
{
Console.WriteLine("Bad CRC");
}
switch (chunkName)
{
case "IHDR" :
writer.Write(byteLength);
writer.Write(chunkType);
writer.Write(data);
Header chunkHeader = new Header(data);
chunkHeader.PrintImageHeader();
break;
case "IDAT" :
if (!foundIDAT)
{
//add transparency header before first IDAT header
byte tnsHeader = CreateTransparencyHeader();
writer.Write(tnsHeader);
foundIDAT = true;
}
writer.Write(byteLength);
writer.Write(chunkType);
writer.Write(data);
break;
default :
writer.Write(byteLength);
writer.Write(chunkType);
writer.Write(data);
break;
}
writer.Write(CRC);
}
reader.Close();
writer.Flush();
writer.Close();
}
public byte CreateTransparencyHeader()
{
byte white = { 0, 0 };
List<byte> header = new List<byte>();
byte length = { 0, 0, 0, 2 }; //length is just two bytes
header.AddRange(length);
header.AddRange(TNS);
header.AddRange(white);
UInt32 crc = GetCRC(TNS, white);
byte crcBytes = { (byte)((crc >> 24) & 0xFF), (byte)((crc >> 16) & 0xFF), (byte)((crc >> 8) & 0xFF), (byte)(crc & 0xFF) };
header.AddRange(crcBytes);
return header.ToArray();
}
public uint GetCRC(byte type, byte bytes)
{
uint crc = 0xffffffff; /* CRC value is 32bit */
//crc = CRC32(byteLength, crc);
crc = CRC32(type, crc);
crc = CRC32(bytes, crc);
crc = Reflect(crc, 32);
crc ^= 0xFFFFFFFF;
return crc;
}
public uint CRC32(byte bytes, uint crc)
{
const uint polynomial = 0x04C11DB7; /* divisor is 32bit */
foreach (byte b in bytes)
{
crc ^= (uint)(Reflect(b, 8) << 24); /* move byte into MSB of 32bit CRC */
for (int i = 0; i < 8; i++)
{
if ((crc & 0x80000000) != 0) /* test for MSB = bit 31 */
{
crc = (uint)((crc << 1) ^ polynomial);
}
else
{
crc <<= 1;
}
}
}
return crc;
}
static public UInt32 Reflect(UInt32 data, int size)
{
UInt32 output = 0;
for (int i = 0; i < size; i++)
{
UInt32 lsb = data & 0x01;
output = (UInt32)((output << 1) | lsb);
data >>= 1;
}
return output;
}
}
public class Header
{
public UInt32 width { get; set; }
public UInt32 height { get; set; }
byte widthBytes { get; set; }
byte heightBytes { get; set; }
public byte depth { get; set; }
public byte colourType { get; set; }
public byte compressionMethod { get; set; }
public byte filterMethod { get; set; }
public byte interlaceMethod { get; set; }
public Header(byte bytes)
{
UInt32 width = (UInt32)((bytes[0] << 24) | (bytes[1] << 16) | (bytes[2] << 8) | bytes[3]);
UInt32 height = (UInt32)((bytes[4] << 24) | (bytes[5] << 16) | (bytes[6] << 8) | bytes[7]);
widthBytes = new byte[4];
Array.Copy(bytes, widthBytes, 4);
heightBytes = new byte[4];
Array.Copy(bytes, 4, heightBytes, 0, 4);
depth = bytes[8];
colourType = bytes[9];
compressionMethod = bytes[10];
filterMethod = bytes[11];
interlaceMethod = bytes[12];
}
public void PrintImageHeader()
{
Console.WriteLine("Width = '{0}', Height = '{1}', Bit Depth = '{2}', Colour Type = '{3}', Compression Method = '{4}', Filter Medthod = '{5}', Interlace Method = '{6}'",
width.ToString(),
height.ToString(),
depth.ToString(),
((COLOUR_TYPE)colourType).ToString(),
compressionMethod.ToString(),
filterMethod.ToString(),
interlaceMethod.ToString()
);
}
public byte GetHeader()
{
List<byte> header = new List<byte>();
header.AddRange(widthBytes);
header.AddRange(heightBytes);
header.Add(depth);
header.Add(colourType);
header.Add(compressionMethod);
header.Add(filterMethod);
header.Add(interlaceMethod);
return header.ToArray();
}
}
public enum COLOUR_TYPE
{
GRAY_SCALE = 0,
TRUE_COLOUR = 2,
INDEXED_COLOUR = 3,
GREY_SCALE_ALPHA = 4,
TRUE_COLOUR_ALPHA = 6
}
}
edited Nov 20 '18 at 12:47
answered Nov 19 '18 at 17:19
jdwengjdweng
16.9k2717
16.9k2717
Sounds like a good question. Doesn't sound like an answer, though.
– TaW
Nov 19 '18 at 19:04
I wasn't sure what the colour mode was so I couldn't complete the code initially. Reading the comments it appears the OP says it file is one byte Grey Scale so I assumed it was colour mode 0. Reading the PNG documentation is looks like adding a transparency chunk before 1st data chunk will solve issue.
– jdweng
Nov 20 '18 at 12:50
Valid answer, but overcomplicated. You can just extract the palette and surgically insert the tRNS chunk, and be done with it. No need for full parsing of the png file at all.
– Nyerguds
Nov 20 '18 at 13:41
Note that technically the tRNS chunk doesn't have to be the full palette size; any unspecified alpha values default to opaque.
– Nyerguds
Nov 20 '18 at 14:03
Does a GreyScale image contain a palette chunk? I only have full color images so I do not know the chunks in a GreyScale image. I only made the tRNS palette two bytes and made the color value 0. Nothing says in the documentation says the Transparent chunk should be the same size as the palette. There is nothing complicated with my code. It is the simplest I could make it and do everything necessary to add the transparency chunk. The only thing extra I added was to display the Header so we can tell the colorour mode.
– jdweng
Nov 20 '18 at 14:10
|
show 5 more comments
Sounds like a good question. Doesn't sound like an answer, though.
– TaW
Nov 19 '18 at 19:04
I wasn't sure what the colour mode was so I couldn't complete the code initially. Reading the comments it appears the OP says it file is one byte Grey Scale so I assumed it was colour mode 0. Reading the PNG documentation is looks like adding a transparency chunk before 1st data chunk will solve issue.
– jdweng
Nov 20 '18 at 12:50
Valid answer, but overcomplicated. You can just extract the palette and surgically insert the tRNS chunk, and be done with it. No need for full parsing of the png file at all.
– Nyerguds
Nov 20 '18 at 13:41
Note that technically the tRNS chunk doesn't have to be the full palette size; any unspecified alpha values default to opaque.
– Nyerguds
Nov 20 '18 at 14:03
Does a GreyScale image contain a palette chunk? I only have full color images so I do not know the chunks in a GreyScale image. I only made the tRNS palette two bytes and made the color value 0. Nothing says in the documentation says the Transparent chunk should be the same size as the palette. There is nothing complicated with my code. It is the simplest I could make it and do everything necessary to add the transparency chunk. The only thing extra I added was to display the Header so we can tell the colorour mode.
– jdweng
Nov 20 '18 at 14:10
Sounds like a good question. Doesn't sound like an answer, though.
– TaW
Nov 19 '18 at 19:04
Sounds like a good question. Doesn't sound like an answer, though.
– TaW
Nov 19 '18 at 19:04
I wasn't sure what the colour mode was so I couldn't complete the code initially. Reading the comments it appears the OP says it file is one byte Grey Scale so I assumed it was colour mode 0. Reading the PNG documentation is looks like adding a transparency chunk before 1st data chunk will solve issue.
– jdweng
Nov 20 '18 at 12:50
I wasn't sure what the colour mode was so I couldn't complete the code initially. Reading the comments it appears the OP says it file is one byte Grey Scale so I assumed it was colour mode 0. Reading the PNG documentation is looks like adding a transparency chunk before 1st data chunk will solve issue.
– jdweng
Nov 20 '18 at 12:50
Valid answer, but overcomplicated. You can just extract the palette and surgically insert the tRNS chunk, and be done with it. No need for full parsing of the png file at all.
– Nyerguds
Nov 20 '18 at 13:41
Valid answer, but overcomplicated. You can just extract the palette and surgically insert the tRNS chunk, and be done with it. No need for full parsing of the png file at all.
– Nyerguds
Nov 20 '18 at 13:41
Note that technically the tRNS chunk doesn't have to be the full palette size; any unspecified alpha values default to opaque.
– Nyerguds
Nov 20 '18 at 14:03
Note that technically the tRNS chunk doesn't have to be the full palette size; any unspecified alpha values default to opaque.
– Nyerguds
Nov 20 '18 at 14:03
Does a GreyScale image contain a palette chunk? I only have full color images so I do not know the chunks in a GreyScale image. I only made the tRNS palette two bytes and made the color value 0. Nothing says in the documentation says the Transparent chunk should be the same size as the palette. There is nothing complicated with my code. It is the simplest I could make it and do everything necessary to add the transparency chunk. The only thing extra I added was to display the Header so we can tell the colorour mode.
– jdweng
Nov 20 '18 at 14:10
Does a GreyScale image contain a palette chunk? I only have full color images so I do not know the chunks in a GreyScale image. I only made the tRNS palette two bytes and made the color value 0. Nothing says in the documentation says the Transparent chunk should be the same size as the palette. There is nothing complicated with my code. It is the simplest I could make it and do everything necessary to add the transparency chunk. The only thing extra I added was to display the Header so we can tell the colorour mode.
– jdweng
Nov 20 '18 at 14:10
|
show 5 more comments
You don't need System.Drawing
for this operation at all.
See, one-bit-per-pixel black-and-white images in PNG format will be either in grayscale format, or paletted.
PNG is made up of chunks which have the following format:
- Four bytes of internal chunk length (big-endian)
- Four ASCII characters of chunk identifier
- The chunk contents (with the length specified in the first part)
- A four-byte CRC hash as consistency check.
Now here's the interesting part: PNG has the rather obscure feature that it supports a tRNS
chunk to be added that sets the alpha for grayscale and paletted format. For grayscale this chunk contains a two-byte value that indicates which grayscale value should be made transparent (which I assume for one bit per pixel should be '1', since that's white), and for paletted format this contains the alpha for each of its palette colours (though it doesn't have to be as long as the palette; any indices not included default to opaque). And since PNG has no overall indexing of its chunks, you can literally just add that in and be done with it.
The code for reading and writing PNG chunks was posted in earlier answers here:
Reading chunks
Writing chunks
You will need to read the IHDR
chunk and check the colour type in the header to see which type of transparency chunk you need to add. An overview of the header format and all colour type possibilities can be found here. Basically, colour type 0 is grayscale, and colour type 3 is paletted, so your image should be one of these two.
For palette transparency, the tRNS
chunk should be added right behind the PLTE
chunk. For grayscale transparency, I believe it should be just before the first IDAT
chunk.
If it's paletted, you do need to check if white is the first or second colour in the palette, so you can set the correct one to transparent.
So once you got that, make a new byte array that's as large as your image plus the added chunk (12 bytes of chunk header and footer and probably 2 bytes of data inside it), and then copy the first part of the file up to the point where the segment should be added into it, then your new segment, and then the rest of the file. Save the bytes array to a file, and you're done.
the link you posted is working from bitmaps and not a png file. Also you have to be careful of the chunk order. The posted code is randomly checking for a chunk type and not sequentially going through the chunks.
– jdweng
Nov 20 '18 at 21:13
No, the link I posted returns a .NetBitmap
object (since its purpose is to load the palette transparency correctly in .Net), but it takes bytes as input. As for the order of the chunks, it does in fact not matter at all for the search procedure. It matters in that the tRNS chunk needs to be right behind the PLTE one, but I specifically mentioned that.
– Nyerguds
Nov 21 '18 at 9:08
add a comment |
You don't need System.Drawing
for this operation at all.
See, one-bit-per-pixel black-and-white images in PNG format will be either in grayscale format, or paletted.
PNG is made up of chunks which have the following format:
- Four bytes of internal chunk length (big-endian)
- Four ASCII characters of chunk identifier
- The chunk contents (with the length specified in the first part)
- A four-byte CRC hash as consistency check.
Now here's the interesting part: PNG has the rather obscure feature that it supports a tRNS
chunk to be added that sets the alpha for grayscale and paletted format. For grayscale this chunk contains a two-byte value that indicates which grayscale value should be made transparent (which I assume for one bit per pixel should be '1', since that's white), and for paletted format this contains the alpha for each of its palette colours (though it doesn't have to be as long as the palette; any indices not included default to opaque). And since PNG has no overall indexing of its chunks, you can literally just add that in and be done with it.
The code for reading and writing PNG chunks was posted in earlier answers here:
Reading chunks
Writing chunks
You will need to read the IHDR
chunk and check the colour type in the header to see which type of transparency chunk you need to add. An overview of the header format and all colour type possibilities can be found here. Basically, colour type 0 is grayscale, and colour type 3 is paletted, so your image should be one of these two.
For palette transparency, the tRNS
chunk should be added right behind the PLTE
chunk. For grayscale transparency, I believe it should be just before the first IDAT
chunk.
If it's paletted, you do need to check if white is the first or second colour in the palette, so you can set the correct one to transparent.
So once you got that, make a new byte array that's as large as your image plus the added chunk (12 bytes of chunk header and footer and probably 2 bytes of data inside it), and then copy the first part of the file up to the point where the segment should be added into it, then your new segment, and then the rest of the file. Save the bytes array to a file, and you're done.
the link you posted is working from bitmaps and not a png file. Also you have to be careful of the chunk order. The posted code is randomly checking for a chunk type and not sequentially going through the chunks.
– jdweng
Nov 20 '18 at 21:13
No, the link I posted returns a .NetBitmap
object (since its purpose is to load the palette transparency correctly in .Net), but it takes bytes as input. As for the order of the chunks, it does in fact not matter at all for the search procedure. It matters in that the tRNS chunk needs to be right behind the PLTE one, but I specifically mentioned that.
– Nyerguds
Nov 21 '18 at 9:08
add a comment |
You don't need System.Drawing
for this operation at all.
See, one-bit-per-pixel black-and-white images in PNG format will be either in grayscale format, or paletted.
PNG is made up of chunks which have the following format:
- Four bytes of internal chunk length (big-endian)
- Four ASCII characters of chunk identifier
- The chunk contents (with the length specified in the first part)
- A four-byte CRC hash as consistency check.
Now here's the interesting part: PNG has the rather obscure feature that it supports a tRNS
chunk to be added that sets the alpha for grayscale and paletted format. For grayscale this chunk contains a two-byte value that indicates which grayscale value should be made transparent (which I assume for one bit per pixel should be '1', since that's white), and for paletted format this contains the alpha for each of its palette colours (though it doesn't have to be as long as the palette; any indices not included default to opaque). And since PNG has no overall indexing of its chunks, you can literally just add that in and be done with it.
The code for reading and writing PNG chunks was posted in earlier answers here:
Reading chunks
Writing chunks
You will need to read the IHDR
chunk and check the colour type in the header to see which type of transparency chunk you need to add. An overview of the header format and all colour type possibilities can be found here. Basically, colour type 0 is grayscale, and colour type 3 is paletted, so your image should be one of these two.
For palette transparency, the tRNS
chunk should be added right behind the PLTE
chunk. For grayscale transparency, I believe it should be just before the first IDAT
chunk.
If it's paletted, you do need to check if white is the first or second colour in the palette, so you can set the correct one to transparent.
So once you got that, make a new byte array that's as large as your image plus the added chunk (12 bytes of chunk header and footer and probably 2 bytes of data inside it), and then copy the first part of the file up to the point where the segment should be added into it, then your new segment, and then the rest of the file. Save the bytes array to a file, and you're done.
You don't need System.Drawing
for this operation at all.
See, one-bit-per-pixel black-and-white images in PNG format will be either in grayscale format, or paletted.
PNG is made up of chunks which have the following format:
- Four bytes of internal chunk length (big-endian)
- Four ASCII characters of chunk identifier
- The chunk contents (with the length specified in the first part)
- A four-byte CRC hash as consistency check.
Now here's the interesting part: PNG has the rather obscure feature that it supports a tRNS
chunk to be added that sets the alpha for grayscale and paletted format. For grayscale this chunk contains a two-byte value that indicates which grayscale value should be made transparent (which I assume for one bit per pixel should be '1', since that's white), and for paletted format this contains the alpha for each of its palette colours (though it doesn't have to be as long as the palette; any indices not included default to opaque). And since PNG has no overall indexing of its chunks, you can literally just add that in and be done with it.
The code for reading and writing PNG chunks was posted in earlier answers here:
Reading chunks
Writing chunks
You will need to read the IHDR
chunk and check the colour type in the header to see which type of transparency chunk you need to add. An overview of the header format and all colour type possibilities can be found here. Basically, colour type 0 is grayscale, and colour type 3 is paletted, so your image should be one of these two.
For palette transparency, the tRNS
chunk should be added right behind the PLTE
chunk. For grayscale transparency, I believe it should be just before the first IDAT
chunk.
If it's paletted, you do need to check if white is the first or second colour in the palette, so you can set the correct one to transparent.
So once you got that, make a new byte array that's as large as your image plus the added chunk (12 bytes of chunk header and footer and probably 2 bytes of data inside it), and then copy the first part of the file up to the point where the segment should be added into it, then your new segment, and then the rest of the file. Save the bytes array to a file, and you're done.
edited Nov 24 '18 at 20:07
answered Nov 20 '18 at 13:30
NyergudsNyerguds
2,96411737
2,96411737
the link you posted is working from bitmaps and not a png file. Also you have to be careful of the chunk order. The posted code is randomly checking for a chunk type and not sequentially going through the chunks.
– jdweng
Nov 20 '18 at 21:13
No, the link I posted returns a .NetBitmap
object (since its purpose is to load the palette transparency correctly in .Net), but it takes bytes as input. As for the order of the chunks, it does in fact not matter at all for the search procedure. It matters in that the tRNS chunk needs to be right behind the PLTE one, but I specifically mentioned that.
– Nyerguds
Nov 21 '18 at 9:08
add a comment |
the link you posted is working from bitmaps and not a png file. Also you have to be careful of the chunk order. The posted code is randomly checking for a chunk type and not sequentially going through the chunks.
– jdweng
Nov 20 '18 at 21:13
No, the link I posted returns a .NetBitmap
object (since its purpose is to load the palette transparency correctly in .Net), but it takes bytes as input. As for the order of the chunks, it does in fact not matter at all for the search procedure. It matters in that the tRNS chunk needs to be right behind the PLTE one, but I specifically mentioned that.
– Nyerguds
Nov 21 '18 at 9:08
the link you posted is working from bitmaps and not a png file. Also you have to be careful of the chunk order. The posted code is randomly checking for a chunk type and not sequentially going through the chunks.
– jdweng
Nov 20 '18 at 21:13
the link you posted is working from bitmaps and not a png file. Also you have to be careful of the chunk order. The posted code is randomly checking for a chunk type and not sequentially going through the chunks.
– jdweng
Nov 20 '18 at 21:13
No, the link I posted returns a .Net
Bitmap
object (since its purpose is to load the palette transparency correctly in .Net), but it takes bytes as input. As for the order of the chunks, it does in fact not matter at all for the search procedure. It matters in that the tRNS chunk needs to be right behind the PLTE one, but I specifically mentioned that.– Nyerguds
Nov 21 '18 at 9:08
No, the link I posted returns a .Net
Bitmap
object (since its purpose is to load the palette transparency correctly in .Net), but it takes bytes as input. As for the order of the chunks, it does in fact not matter at all for the search procedure. It matters in that the tRNS chunk needs to be right behind the PLTE one, but I specifically mentioned that.– Nyerguds
Nov 21 '18 at 9:08
add a comment |
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Some of your past answers have not been well-received, and you're in danger of being blocked from answering.
Please pay close attention to the following guidance:
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53374979%2fadd-tranparency-to-huge-image-c-sharp%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Have you tried using
SetPixel(...)
to achieve this?– Martin Parkin
Nov 19 '18 at 12:54
Not yet. But SetPixel() is a bit slow. For 19860x28080 it will be last about 15min
– Bartek Chyży
Nov 19 '18 at 12:56
2
I'm sorry, you didn't say that it needed to be speedy in your question...
– Martin Parkin
Nov 19 '18 at 12:56
Right, my bad. I will have to process about 3000 images like that. :/
– Bartek Chyży
Nov 19 '18 at 12:58
You need to play with the colormap instead. ColorMap you technically change the table saying color ID "x" is now the RGB "y". On such large file it should be 4-5 seconds tops to change the whole thing. You may want to check my answer here
– Franck
Nov 19 '18 at 13:54