public Stream GetImage(State p_state)
        {
            string fname = ConfigurationManager.AppSettings["JP2CachePath"] + p_state.File;
            if (!File.Exists(fname))
            {
                throw new IIIFException(getErrorXml(new Error { Code = "i3f_200", Message = p_state.File + " does not exist" }));
            }

            try
            {
                return GetImageUsingKDUExpand(p_state);
            }
            catch (IIIFException e)
            {
                throw e;
            }
            catch (Exception e)
            {
                throw e;
            }
        }
        private Stream GetImageUsingKDUExpand(State p_state)
        {
            try
            {
                string fname = ConfigurationManager.AppSettings["JP2CachePath"] + p_state.File;
                float starty = 0;
                float startx = 0;
                double tileheight = 1;
                double tilewidth = 1;

                string[] regions = null;
                float scale = 0;
                float scalex = 0;
                float scaley = 0;

                Metadata imageSize = GetJP2Metadata(fname);

                regions = p_state.Region.Split(new char[] { ',' });

                if (regions.Length == 4)
                {
                    if (!regions[0].StartsWith("%"))
                    {
                        tilewidth = int.Parse(regions[2]);
                        tileheight = int.Parse(regions[3]);
                    }
                    else
                    {
                        tilewidth = double.Parse(regions[2]) * imageSize.Width;
                        tileheight = double.Parse(regions[3]) * imageSize.Height;
                    }
                }
                // assume all region returned
                else
                {
                    startx = 0;
                    starty = 0;
                }

                float wScale = (float)p_state.Width / imageSize.Width;
                float hScale = (float)p_state.Height / imageSize.Height;

                scale = scalex = scaley = 1;
                startx = 0;
                starty = 0;

                switch (p_state.SizeType)
                {
                    case "resize":
                        if (p_state.Region.Equals("all"))
                        {
                            scalex = wScale;
                            scaley = hScale;
                            if (p_state.SizeType.Equals("best"))
                            {
                                if (scalex < scaley)
                                {
                                    scale = scalex;
                                }
                                else
                                {
                                    scale = scaley;
                                }
                            }
                            else
                            {
                                if (scalex > scaley)
                                {
                                    scale = scalex;
                                }
                                else
                                {
                                    scale = scaley;
                                }
                            }
                            tilewidth = Convert.ToInt32(imageSize.Width * scale);
                            tileheight = Convert.ToInt32(imageSize.Height * scale);
                        }
                        else if (p_state.Width != 0 && p_state.Height != 0)
                        {
                            if (tilewidth > 0)
                                scalex = (float)(p_state.Width / tilewidth);
                            else
                                scalex = wScale;

                            if (tileheight > 0)
                                scaley = (float)(p_state.Height / tileheight);
                            else
                                scaley = hScale;

                            if (p_state.Width == p_state.Height)
                            {
                                scale = scalex; // both scales should be the same so shouldn't matter
                            }
                            else if (p_state.Width > p_state.Height)
                            {
                                scale = scalex;
                            }
                            else
                            {
                                scale = scaley;
                            }
                        }
                        else if (p_state.Width > p_state.Height)
                        {
                            if (tilewidth > 0)
                                scale = (float)(p_state.Width / tilewidth);
                            else
                                scale = wScale;

                            scaley = scalex = scale;
                        }
                        else
                        {
                            if (tileheight > 0)
                                scale = (float)(p_state.Height / tileheight);
                            else
                                scale = hScale;
                        }

                        // get all
                        if (!p_state.SizeType.Equals("resize") && p_state.Region.Equals("all"))
                        {
                            if (p_state.Width != 0)
                                tilewidth = p_state.Width;

                            if (p_state.Height != 0)
                                tileheight = p_state.Height;
                        }

                        break;
                    case "best":
                        if (tilewidth > 0)
                            scalex = (float)(p_state.Width / tilewidth);
                        else
                            scalex = wScale;

                        if (tileheight > 0)
                            scaley = (float)(p_state.Height / tileheight);
                        else
                            scaley = hScale;

                        if (scalex < scaley)
                            scale = scalex;
                        else
                            scale = scaley;

                        // get all
                        if (p_state.Region.Equals("all"))
                        {
                            tileheight = int.Parse(Math.Round(imageSize.Height * scale).ToString());
                            tilewidth = int.Parse(Math.Round(imageSize.Width * scale).ToString());
                        }
                        break;
                    case "proportion":
                        //scale = scalex = scaley = p_state.Size;
                        scale = p_state.Size;

                        // get all
                        if (p_state.Region.Equals("all"))
                        {
                            tileheight = int.Parse(Math.Round(imageSize.Height * scale).ToString());
                            tilewidth = int.Parse(Math.Round(imageSize.Width * scale).ToString());
                        }

                        break;
                    default:
                        if (wScale < hScale)
                            scale = wScale;
                        else
                            scale = hScale;

                        scalex = scaley = scale;
                        break;
                }

                regions = p_state.Region.Split(new char[] { ',' });

                string region = "";
                if (regions.Length == 4)
                {
                    // for percentage regions
                    if (p_state.Region.StartsWith("%"))
                    {
                        startx = float.Parse(regions[0].Substring(1));
                        starty = float.Parse(regions[1]);

                        tilewidth = float.Parse(regions[2]);
                        tileheight = float.Parse(regions[3]);
                    }
                    else
                    {
                        startx = float.Parse(regions[0]) / imageSize.Width;
                        starty = float.Parse(regions[1]) / imageSize.Height;

                        tilewidth = float.Parse(regions[2]) / imageSize.Width;
                        tileheight = float.Parse(regions[3]) / imageSize.Height;
                    }

                    region = "{" + starty + "," + startx + "},{" + tileheight + "," + tilewidth + "}";
                }

                if (starty > 1 || startx > 1 || startx + tilewidth < 0 || starty + tileheight < 0)
                {
                    throw new IIIFException(getErrorXml(new Error { Code = "i3f_400", Message = "Invalid region specified" }));
                }

                // try to get the closest possible size to the one we want in order to reduce the tile size
                // when creating an image tile
                int reduce = Convert.ToInt32(1 / scale);
                if (reduce > 0)
                    reduce = Convert.ToInt32(Math.Floor(Math.Log(reduce, 2.5)));
                p_state.Reduce = reduce;

                string outputFile = ConfigurationManager.AppSettings["JP2CachePath"] + p_state.Target.Replace(".jpg", ".bmp");

                ProcessStartInfo start = new ProcessStartInfo();
                start.FileName = ConfigurationManager.AppSettings["ExpanderPath"];
                start.UseShellExecute = false;
                start.RedirectStandardOutput = false;
                start.RedirectStandardError = true;
                start.CreateNoWindow = true;

                start.Arguments = "-resilient -quiet -i \"" + fname + "\" -o \"" +
                    outputFile +
                    "\" -reduce " + reduce +
                    (string.IsNullOrEmpty(region) ? "" : " -region " + region);

                Process proc = Process.Start(start);
                proc.WaitForExit();

                BufferedStream bs = new BufferedStream(new FileStream(outputFile, FileMode.Open));

                int length = (int)bs.Length;
                byte[] imageBits = new byte[length];

                // read the digital bits of the image into the byte array
                bs.Read(imageBits, 0, length);

                Image img = Image.FromStream(bs);

                Bitmap bmp = new Bitmap(img);

                MemoryStream stream = new MemoryStream();
                bmp.Save(stream, ImageFormat.Jpeg);

                // remove temporary bmp file
                img.Dispose();
                bs.Close();
                bs.Dispose();

                File.Delete(outputFile);

                return stream;
            }
            catch (Exception)
            {
                throw;
            }
        }
        private void GetImage(IAsyncResult p_res)
        {
            ImageRequestState state = (ImageRequestState)p_res.AsyncState;
            if (state.err != null)
            {
                asynch.CompleteCall();
            }
            else {
                lock (lockobj)
                {
                    try
                    {
                        Extractor kdu = new Extractor();
                        State kdu_state = new State();

                        kdu_state.File = state.identifier.EndsWith(".jp2") ? state.identifier : state.identifier + ".jp2";
                        kdu_state.SizeType = state.sizeType;
                        kdu_state.Size = string.IsNullOrEmpty(state.size) ? 0 : float.Parse(state.size);
                        kdu_state.Width = string.IsNullOrEmpty(state.width) ? 0 : int.Parse(state.width);
                        kdu_state.Height = string.IsNullOrEmpty(state.height) ? 0 : int.Parse(state.height);
                        kdu_state.Region = state.region;
                        kdu_state.Format = state.format;
                        kdu_state.Quality = state.quality;

                        Stream stream = kdu.GetImage(kdu_state);
                        Image img = Image.FromStream(stream);

                        if (state.sizeType.Equals("resize"))
                        {
                            int sizeWidth = img.Width;
                            int sizeHeight = img.Height;

                            if (!string.IsNullOrEmpty(state.width) && !string.IsNullOrEmpty(state.height))
                            {
                                sizeWidth = Convert.ToInt32(state.width);
                                sizeHeight = Convert.ToInt32(state.height);
                            }
                            else if (!string.IsNullOrEmpty(state.width))
                            {
                                sizeWidth = Convert.ToInt32(state.width);
                                sizeHeight = (int)(((float)sizeWidth / img.Width) * sizeHeight);
                            }
                            else if (!string.IsNullOrEmpty(state.height))
                            {
                                sizeHeight = Convert.ToInt32(state.height);
                                sizeWidth = (int)(((float)sizeHeight / img.Height) * sizeWidth);
                            }

                            img = ResizeImage(img, sizeWidth, sizeHeight);
                        }
                        else if (state.sizeType.Equals("best"))
                        {
                            int sizeWidth = Convert.ToInt32(state.width);
                            int sizeHeight = Convert.ToInt32(state.height);
                            double regionWidth = img.Width;
                            if (!string.IsNullOrEmpty(state.regionWidth))
                                regionWidth = Convert.ToDouble(state.regionWidth);
                            double regionHeight = img.Height;
                            if (!string.IsNullOrEmpty(state.regionHeight))
                                regionHeight = Convert.ToDouble(state.regionHeight);

                            double scalex = sizeWidth / regionWidth;
                            double scaley = sizeHeight / regionHeight;

                            if (scalex < scaley)
                                img = ResizeImage(img, (int)(regionWidth * scalex), (int)(regionHeight * scalex));
                            else
                                img = ResizeImage(img, (int)(regionWidth * scaley), (int)(regionHeight * scaley));
                        }
                        else if (state.sizeType.Equals("proportion"))
                        {
                            double regionWidth = 0;
                            double regionHeight = 0;

                            if (state.region.Equals("all"))
                            {
                                XmlDocument doc = new XmlDocument();
                                doc.LoadXml(kdu.GetMetadata(state.identifier.EndsWith(".jp2") ? state.identifier : state.identifier + ".jp2"));

                                regionWidth = Convert.ToInt32(doc.GetElementsByTagName("Width")[0].InnerText);
                                regionHeight = Convert.ToInt32(doc.GetElementsByTagName("Height")[0].InnerText);
                            }
                            else if (state.region.StartsWith("%"))
                            {
                                XmlDocument doc = new XmlDocument();
                                doc.LoadXml(kdu.GetMetadata(state.identifier.EndsWith(".jp2") ? state.identifier : state.identifier + ".jp2"));

                                regionWidth = Convert.ToDouble(state.regionWidth) * Convert.ToInt32(doc.GetElementsByTagName("Width")[0].InnerText);
                                regionHeight = Convert.ToDouble(state.regionHeight) * Convert.ToInt32(doc.GetElementsByTagName("Height")[0].InnerText);
                            }
                            else
                            {
                                regionWidth = Convert.ToInt32(state.regionWidth);
                                regionHeight = Convert.ToInt32(state.regionHeight);
                            }

                            regionWidth *= Convert.ToDouble(state.size);
                            regionHeight *= Convert.ToDouble(state.size);

                            if (regionWidth < 1 || regionHeight < 1)
                            {
                                state.err = new error();
                                state.err.parameter = "region / size";
                                state.err.text = "Invalid region /size specified";
                                state.err.statusCode = System.Net.HttpStatusCode.BadRequest;
                            }
                            else
                            {
                                img = ResizeImage(img, (int)regionWidth, (int)regionHeight);
                            }
                        }

                        if (state.err == null)
                        {
                            if (!string.IsNullOrEmpty(state.quality))
                            {
                                if (state.quality.Equals("grey"))
                                    img = MakeGrayscale(new Bitmap(img));
                                else if (state.quality.Equals("bitonal"))
                                    img = MakeBitonal(new Bitmap(img));
                            }

                            switch (state.rotation)
                            {
                                case "90": img.RotateFlip(RotateFlipType.Rotate90FlipNone); break;
                                case "180": img.RotateFlip(RotateFlipType.Rotate180FlipNone); break;
                                case "270": img.RotateFlip(RotateFlipType.Rotate270FlipNone); break;
                            }

                            _bitmap = new Bitmap(img);
                        }
                    }
                    catch (IIIFException e)
                    {
                        state.err = new error();

                        try
                        {
                            XmlDocument doc = new XmlDocument();
                            doc.LoadXml(e.Message);

                            if (doc.GetElementsByTagName("Code")[0].InnerText.Contains("i3f_200"))
                            {
                                state.err.parameter = "identifier";
                                state.err.text = doc.GetElementsByTagName("Message")[0].InnerText;
                                state.err.statusCode = System.Net.HttpStatusCode.NotFound;
                            }
                            else if (doc.GetElementsByTagName("Code")[0].InnerText.Contains("i3f_400"))
                            {
                                state.err.parameter = "region";
                                state.err.text = doc.GetElementsByTagName("Message")[0].InnerText;
                                state.err.statusCode = System.Net.HttpStatusCode.BadRequest;
                            }
                            else
                            {
                                state.err.parameter = "unknown";
                                state.err.text = e.Message;
                                state.err.statusCode = System.Net.HttpStatusCode.BadRequest;
                            }
                        }
                        catch (Exception ex)
                        {
                            state.err.parameter = "unknown";
                            state.err.text = ex.Message;
                            state.err.statusCode = System.Net.HttpStatusCode.BadRequest;
                        }
                    }
                    catch (Exception e)
                    {
                        state.err = new error();
                        state.err.parameter = "unknown";
                        state.err.text = e.Message;
                        state.err.statusCode = System.Net.HttpStatusCode.BadRequest;
                    }
                    finally
                    {
                        asynch.CompleteCall();
                    }
                }
            }
        }