Example #1
0
		public PNG_INFO png_get_PLTE(ref png_color[] palette)
		{
			if((info_ptr_valid&PNG_INFO.PLTE)!=PNG_INFO.PLTE) return PNG_INFO.None;
			palette=info_ptr_palette;
			return PNG_INFO.PLTE;
		}
Example #2
0
		public void png_set_PLTE(png_color[] palette)
		{
			if(palette==null||palette.Length==0)
			{
				this.palette=info_ptr_palette=null;
				info_ptr_valid&=~PNG_INFO.PLTE;
				return;
			}

			if(palette.Length>PNG.MAX_PALETTE_LENGTH)
			{
				if(info_ptr_color_type==PNG_COLOR_TYPE.PALETTE) throw new PNG_Exception("Invalid palette length");
				else
				{
					Debug.WriteLine("Invalid palette length");
					return;
				}
			}

			this.palette=info_ptr_palette=palette;
			info_ptr_valid|=PNG_INFO.PLTE;
		}
Example #3
0
		// read and check the palette
		void png_handle_PLTE(uint length)
		{
			if((mode&PNG_MODE.HAVE_IHDR)!=PNG_MODE.HAVE_IHDR) throw new PNG_Exception("Missing IHDR before PLTE");
			else if((mode&PNG_MODE.HAVE_IDAT)==PNG_MODE.HAVE_IDAT)
			{
				Debug.WriteLine("Invalid PLTE after IDAT");
				png_crc_finish(length);
				return;
			}
			else if((mode&PNG_MODE.HAVE_PLTE)==PNG_MODE.HAVE_PLTE) throw new PNG_Exception("Duplicate PLTE chunk");

			mode|=PNG_MODE.HAVE_PLTE;

			if((color_type&PNG_COLOR_TYPE.COLOR_MASK)!=PNG_COLOR_TYPE.COLOR_MASK)
			{
				Debug.WriteLine("Ignoring PLTE chunk in grayscale PNG");
				png_crc_finish(length);
				return;
			}

			png_color[] palette=new png_color[PNG.MAX_PALETTE_LENGTH];

			if((length>3*PNG.MAX_PALETTE_LENGTH)||length%3!=0)
			{
				if(color_type!=PNG_COLOR_TYPE.PALETTE)
				{
					Debug.WriteLine("Invalid palette chunk");
					png_crc_finish(length);
					return;
				}
				else throw new PNG_Exception("Invalid palette chunk");
			}

			ushort num=(ushort)(length/3);

			for(ushort i=0; i<num; i++)
			{
				png_crc_read(buf4, 3);
				palette[i].red=buf4[0];
				palette[i].green=buf4[1];
				palette[i].blue=buf4[2];
			}

			// If we actually NEED the PLTE chunk (ie for a paletted image), we do
			// whatever the normal CRC configuration tells us. However, if we
			// have an RGB image, the PLTE can be considered ancillary, so
			// we will act as though it is.
			png_crc_finish(0);

			png_set_PLTE(palette);

			if(color_type==PNG_COLOR_TYPE.PALETTE)
			{
				if((info_ptr_valid&PNG_INFO.tRNS)==PNG_INFO.tRNS)
				{
					if(num_trans>num)
					{
						Debug.WriteLine("Truncating incorrect tRNS chunk length");
						byte[] newtrans=new byte[num];
						Array.Copy(trans_alpha, newtrans, num);
						trans_alpha=newtrans;
						num_trans=num;
					}
					if(info_ptr_num_trans>num)
					{
						Debug.WriteLine("Truncating incorrect info tRNS chunk length");
						byte[] newtrans=new byte[num];
						Array.Copy(trans_alpha, newtrans, num);
						trans_alpha=newtrans;
						info_ptr_num_trans=num;
					}
				}
			}
		}
Example #4
0
		// write the palette. We are careful not to trust png_color to be in the
		// correct order for PNG, so people can redefine it to any convenient
		// structure.
		void png_write_PLTE(png_color[] palette)
		{
			uint num_pal=0;
			if(palette!=null) num_pal=(uint)palette.Length;

			if(((mng_features_permitted&PNG_FLAG_MNG.EMPTY_PLTE)!=PNG_FLAG_MNG.EMPTY_PLTE&&num_pal==0)||num_pal>256)
			{
				if(color_type==PNG_COLOR_TYPE.PALETTE) throw new PNG_Exception("Invalid number of colors in palette");
				else
				{
					Debug.WriteLine("Invalid number of colors in palette");
					return;
				}
			}

			if((color_type&PNG_COLOR_TYPE.COLOR_MASK)!=PNG_COLOR_TYPE.COLOR_MASK)
			{
				Debug.WriteLine("Ignoring request to write a PLTE chunk in grayscale PNG");
				return;
			}

			png_write_chunk_start(PNG.PLTE, num_pal*3);
			for(uint i=0; i<num_pal; i++)
			{
				buf4[0]=palette[i].red;
				buf4[1]=palette[i].green;
				buf4[2]=palette[i].blue;
				png_write_chunk_data(buf4, 3);
			}
			png_write_chunk_end();
			mode|=PNG_MODE.HAVE_PLTE;
		}
Example #5
0
 // Save typing and make code easier to understand
 static int PNG_COLOR_DIST(png_color c1, png_color c2)
 {
     return(Math.Abs((int)c1.red - c2.red) + Math.Abs((int)c1.green - c2.green) + Math.Abs((int)c1.blue - c2.blue));
 }
Example #6
0
		// Expands a palette row to an RGB or RGBA row depending
		// upon whether you supply trans_alpha and num_trans.
		static unsafe void png_do_expand_palette(ref png_row_info row_info, byte[] row, png_color[] palette, byte[] trans_alpha)
		{
			int shift, value;
			uint row_width=row_info.width;

			if(row_info.color_type!=PNG_COLOR_TYPE.PALETTE) return;

			fixed(byte* row_=row)
			{
				byte* sp=row_+1; // skip filter value
				byte* dp=row_+1; // skip filter value

				if(row_info.bit_depth<8)
				{
					switch(row_info.bit_depth)
					{
						case 1:
							{
								sp+=(row_width-1)>>3;
								dp+=row_width-1;
								shift=7-(int)((row_width+7)&0x07);
								for(uint i=0; i<row_width; i++)
								{
									if(((*sp>>shift)&0x01)==0x01) *dp=1;
									else *dp=0;
									if(shift==7)
									{
										shift=0;
										sp--;
									}
									else shift++;

									dp--;
								}
							}
							break;
						case 2:
							{
								sp+=(row_width-1)>>2;
								dp+=row_width-1;
								shift=(int)((3-((row_width+3)&0x03))<<1);
								for(uint i=0; i<row_width; i++)
								{
									value=(*sp>>shift)&0x03;
									*dp=(byte)value;
									if(shift==6)
									{
										shift=0;
										sp--;
									}
									else shift+=2;

									dp--;
								}
							}
							break;
						case 4:
							{
								sp+=(row_width-1)>>1;
								dp+=row_width-1;
								shift=(int)((row_width&0x01)<<2);
								for(uint i=0; i<row_width; i++)
								{
									value=(*sp>>shift)&0x0f;
									*dp=(byte)value;
									if(shift==4)
									{
										shift=0;
										sp--;
									}
									else shift+=4;

									dp--;
								}
							}
							break;
					}
					row_info.bit_depth=8;
					row_info.pixel_depth=8;
					row_info.rowbytes=row_width;
				} // if(row_info.bit_depth<8)

				if(row_info.bit_depth==8)
				{
					sp=row_+1;
					dp=row_+1;
					if(trans_alpha!=null&&trans_alpha.Length!=0)
					{
						sp+=row_width-1;
						dp+=(row_width<<2)-1;

						for(uint i=0; i<row_width; i++)
						{
							if(*sp>=trans_alpha.Length) *dp--=0xff;
							else *dp--=trans_alpha[*sp];
							*dp--=palette[*sp].blue;
							*dp--=palette[*sp].green;
							*dp--=palette[*sp].red;
							sp--;
						}
						row_info.bit_depth=8;
						row_info.pixel_depth=32;
						row_info.rowbytes=row_width*4;
						row_info.color_type=PNG_COLOR_TYPE.RGB_ALPHA;
						row_info.channels=4;
					}
					else
					{
						sp+=row_width-1;
						dp+=(row_width*3)-1;

						for(uint i=0; i<row_width; i++)
						{
							*dp--=palette[*sp].blue;
							*dp--=palette[*sp].green;
							*dp--=palette[*sp].red;
							sp--;
						}
						row_info.bit_depth=8;
						row_info.pixel_depth=24;
						row_info.rowbytes=row_width*3;
						row_info.color_type=PNG_COLOR_TYPE.RGB;
						row_info.channels=3;
					}
				}
			}
		}
Example #7
0
		// Build a grayscale palette. Palette is assumed to be 1 << bit_depth
		// large of png_color. This lets grayscale images be treated as
		// paletted. Most useful for gamma correction and simplification
		// of code.
		public static void png_build_grayscale_palette(int bit_depth, png_color[] palette)
		{
			if(palette==null||palette.Length==0) return;

			int num_palette;
			int color_inc;

			switch(bit_depth)
			{
				case 1:
					num_palette=2;
					color_inc=0xff;
					break;
				case 2:
					num_palette=4;
					color_inc=0x55;
					break;
				case 4:
					num_palette=16;
					color_inc=0x11;
					break;
				case 8:
					num_palette=256;
					color_inc=1;
					break;
				default:
					num_palette=0;
					color_inc=0;
					break;
			}

			for(int i=0, v=0; i<num_palette; i++, v+=color_inc)
			{
				palette[i].red=(byte)v;
				palette[i].green=(byte)v;
				palette[i].blue=(byte)v;
			}
		}
Example #8
0
		// Prior to libpng-1.4.2, this was png_set_dither().
		public void png_set_quantize(png_color[] palette, byte maximum_colors, ushort[] histogram, bool full_quantize)
		{
			transformations|=PNG_TRANSFORMATION.QUANTIZE;

			if(!full_quantize)
			{
				quantize_index=new byte[palette.Length];
				for(int i=0; i<palette.Length; i++) quantize_index[i]=(byte)i;
			}

			if(palette.Length>maximum_colors)
			{
				if(histogram!=null)
				{
					// This is easy enough, just throw out the least used colors.
					// Perhaps not the best solution, but good enough.

					// initialize an array to sort colors
					byte[] quantize_sort=new byte[palette.Length];

					// initialize the quantize_sort array
					for(int i=0; i<palette.Length; i++) quantize_sort[i]=(byte)i;

					// Find the least used palette entries by starting a
					// bubble sort, and running it until we have sorted
					// out enough colors. Note that we don't care about
					// sorting all the colors, just finding which are
					// least used.
					for(int i=palette.Length-1; i>=maximum_colors; i--)
					{
						bool done=true; // to stop early if the list is pre-sorted

						for(int j=0; j<i; j++)
						{
							if(histogram[quantize_sort[j]]<histogram[quantize_sort[j+1]])
							{
								byte t=quantize_sort[j];
								quantize_sort[j]=quantize_sort[j+1];
								quantize_sort[j+1]=t;
								done=false;
							}
						}
						if(done) break;
					}

					// swap the palette around, and set up a table, if necessary
					if(full_quantize)
					{
						int j=palette.Length;

						// put all the useful colors within the max, but don't move the others
						for(int i=0; i<maximum_colors; i++)
						{
							if(quantize_sort[i]>=maximum_colors)
							{
								do { j--; } while(quantize_sort[j]>=maximum_colors);
								palette[i]=palette[j];
							}
						}
					}
					else
					{
						int j=palette.Length;

						// move all the used colors inside the max limit, and develop a translation table
						for(int i=0; i<maximum_colors; i++)
						{
							// only move the colors we need to
							if(quantize_sort[i]>=maximum_colors)
							{
								do { j--; } while(quantize_sort[j]>=maximum_colors);

								png_color tmp_color=palette[j];
								palette[j]=palette[i];
								palette[i]=tmp_color;

								// indicate where the color went
								quantize_index[j]=(byte)i;
								quantize_index[i]=(byte)j;
							}
						}

						// find closest color for those colors we are not using
						for(int i=0; i<palette.Length; i++)
						{
							if(quantize_index[i]>=maximum_colors)
							{
								// find the closest color to one we threw out
								int d_index=quantize_index[i];
								int min_d=PNG_COLOR_DIST(palette[d_index], palette[0]);
								int min_k=0;
								for(int k=1; k<maximum_colors; k++)
								{
									int d=PNG_COLOR_DIST(palette[d_index], palette[k]);

									if(d<min_d)
									{
										min_d=d;
										min_k=k;
									}
								}
								// point to closest color
								quantize_index[i]=(byte)min_k;
							}
						}
					}
					quantize_sort=null;
				}
				else // if(histogram!=null)
				{
					// This is much harder to do simply (and quickly). Perhaps
					// we need to go through a median cut routine, but those
					// don't always behave themselves with only a few colors
					// as input. So we will just find the closest two colors,
					// and throw out one of them (chosen somewhat randomly).
					// [We don't understand this at all, so if someone wants to
					// work on improving it, be our guest - AED, GRP]

					// initialize palette index arrays
					byte[] index_to_palette=new byte[palette.Length];
					byte[] palette_to_index=new byte[palette.Length];

					// initialize the sort array
					for(int i=0; i<palette.Length; i++)
					{
						index_to_palette[i]=(byte)i;
						palette_to_index[i]=(byte)i;
					}

					png_dsort[] hash=new png_dsort[769];

					int num_new_palette=palette.Length;

					// initial wild guess at how far apart the farthest pixel
					// pair we will be eliminating will be. Larger
					// numbers mean more areas will be allocated, Smaller
					// numbers run the risk of not saving enough data, and
					// having to do this all over again.
					//
					// I have not done extensive checking on this number.
					int max_d=96;

					png_dsort t=null;

					while(num_new_palette>maximum_colors)
					{
						for(int i=0; i<num_new_palette-1; i++)
						{
							for(int j=i+1; j<num_new_palette; j++)
							{
								int d=PNG_COLOR_DIST(palette[i], palette[j]);

								if(d<=max_d)
								{
									try
									{
										t=new png_dsort();
									}
									catch(Exception)
									{
										t=null;
										break;
									}
									t.next=hash[d];
									t.left=(byte)i;
									t.right=(byte)j;
									hash[d]=t;
								}
							}
							if(t==null) break;
						}

						if(t!=null)
						{
							for(int i=0; i<=max_d; i++)
							{
								if(hash[i]!=null)
								{
									for(png_dsort p=hash[i]; p!=null; p=p.next)
									{
										if(index_to_palette[p.left]<num_new_palette&&index_to_palette[p.right]<num_new_palette)
										{
											int j, next_j;

											if((num_new_palette&0x01)==0x01)
											{
												j=p.left;
												next_j=p.right;
											}
											else
											{
												j=p.right;
												next_j=p.left;
											}

											num_new_palette--;
											palette[index_to_palette[j]]=palette[num_new_palette];
											if(!full_quantize)
											{
												for(int k=0; k<palette.Length; k++)
												{
													if(quantize_index[k]==index_to_palette[j]) quantize_index[k]=index_to_palette[next_j];
													if(quantize_index[k]==num_new_palette) quantize_index[k]=index_to_palette[j];
												}
											}

											index_to_palette[palette_to_index[num_new_palette]]=index_to_palette[j];
											palette_to_index[index_to_palette[j]]=palette_to_index[num_new_palette];

											index_to_palette[j]=(byte)num_new_palette;
											palette_to_index[num_new_palette]=(byte)j;
										}
										if(num_new_palette<=maximum_colors) break;
									}
									if(num_new_palette<=maximum_colors) break;
								}
							} // for
						}

						for(int i=0; i<769; i++)
						{
							if(hash[i]!=null)
							{
								png_dsort p=hash[i];
								while(p!=null)
								{
									t=p.next;
									p.next=null;
									p=t;
								}
							}
							hash[i]=null;
						}
						max_d+=96;
					}
					t=null;
					hash=null;
					palette_to_index=null;
					index_to_palette=null;
				}

				png_color[] tmppal=new png_color[maximum_colors];
				Array.Copy(palette, tmppal, maximum_colors);
				palette=tmppal;

			} // if(num_palette>maximum_colors)

			if(this.palette==null) this.palette=palette;
			if(!full_quantize) return;

			int total_bits=PNG_QUANTIZE_RED_BITS+PNG_QUANTIZE_GREEN_BITS+PNG_QUANTIZE_BLUE_BITS;
			int num_red=1<<PNG_QUANTIZE_RED_BITS;
			int num_green=1<<PNG_QUANTIZE_GREEN_BITS;
			int num_blue=1<<PNG_QUANTIZE_BLUE_BITS;
			uint num_entries=(uint)(1<<total_bits);

			palette_lookup=new byte[num_entries];

			byte[] distance=new byte[num_entries];
			for(int i=0; i<num_entries; i++) distance[i]=0xff; //memset(distance, 0xff, num_entries*sizeof(byte));

			for(int i=0; i<palette.Length; i++)
			{
				int r=palette[i].red>>(8-PNG_QUANTIZE_RED_BITS);
				int g=palette[i].green>>(8-PNG_QUANTIZE_GREEN_BITS);
				int b=palette[i].blue>>(8-PNG_QUANTIZE_BLUE_BITS);

				for(int ir=0; ir<num_red; ir++)
				{
					// int dr=abs(ir-r);
					int dr=(ir>r)?ir-r:r-ir;
					int index_r=ir<<(PNG_QUANTIZE_BLUE_BITS+PNG_QUANTIZE_GREEN_BITS);

					for(int ig=0; ig<num_green; ig++)
					{
						// int dg=abs(ig-g);
						int dg=(ig>g)?ig-g:g-ig;
						int dt=dr+dg;
						int dm=(dr>dg)?dr:dg;
						int index_g=index_r|ig<<PNG_QUANTIZE_BLUE_BITS;

						for(int ib=0; ib<num_blue; ib++)
						{
							int d_index=index_g|ib;
							// int db=abs(ib-b);
							int db=(ib>b)?ib-b:b-ib;
							int dmax=(dm>db)?dm:db;
							int d=dmax+dt+db;

							if(d<distance[d_index])
							{
								distance[d_index]=(byte)d;
								palette_lookup[d_index]=(byte)i;
							}
						}
					}
				}
			}

			distance=null;
		}
Example #9
0
		public void png_set_dither(png_color[] palette, byte maximum_colors, ushort[] histogram, bool full_dither)
		{
			png_set_quantize(palette, maximum_colors, histogram, full_dither);
		}
Example #10
0
		// Save typing and make code easier to understand
		static int PNG_COLOR_DIST(png_color c1, png_color c2)
		{
			return Math.Abs((int)c1.red-c2.red)+Math.Abs((int)c1.green-c2.green)+Math.Abs((int)c1.blue-c2.blue);
		}