Example #1
0
        /// <summary>
        /// Start rendering a region of a display list to the specified destination.
        /// </summary>
        /// <param name="context">The rendering context.</param>
        /// <param name="displayList">The native display list that should be rendered.</param>
        /// <param name="region">The region that should be rendered.</param>
        /// <param name="zoom">The scale at which the region will be rendered. This will determine the size in pixel of the image.</param>
        /// <param name="pixelStorage">The address of the buffer where the pixel data will be written. There must be enough space available to write the values for all the pixels, otherwise this will fail catastrophically!</param>
        /// <param name="pixelFormat">The format of the pixel data.</param>
        /// <param name="pageBounds">The bounds of the page being rendererd.</param>
        /// <param name="clipToPageBounds">A boolean value indicating whether the rendered image should be clipped to the original page's bounds. This can be relevant if the page has been "cropped" by altering its mediabox, but otherwise leaving the contents untouched.</param>
        public void Render(IntPtr context, MuPDFDisplayList displayList, Rectangle region, float zoom, IntPtr pixelStorage, PixelFormats pixelFormat, Rectangle pageBounds, bool clipToPageBounds)
        {
            lock (RenderDataLock)
            {
                //Reset the cookie.
                unsafe
                {
                    Cookie *cookie = (Cookie *)Cookie;

                    cookie->abort        = 0;
                    cookie->errors       = 0;
                    cookie->incomplete   = 0;
                    cookie->progress     = 0;
                    cookie->progress_max = 0;
                }

                //Set up all the rendering data.
                CurrentRenderData.Context          = context;
                CurrentRenderData.DisplayList      = displayList;
                CurrentRenderData.Region           = region;
                CurrentRenderData.Zoom             = zoom;
                CurrentRenderData.PixelStorage     = pixelStorage;
                CurrentRenderData.PixelFormat      = pixelFormat;
                CurrentRenderData.PageBounds       = pageBounds;
                CurrentRenderData.ClipToPageBounds = clipToPageBounds;
                SignalToThread.Set();
            }
        }
Example #2
0
        /// <summary>
        /// Write (part of) a page to an image stream in the specified format.
        /// </summary>
        /// <param name="pageNumber">The number of the page to render (starting at 0).</param>
        /// <param name="region">The region of the page to render in page units.</param>
        /// <param name="zoom">The scale at which the page will be rendered. This will determine the size in pixel of the image.</param>
        /// <param name="pixelFormat">The format of the pixel data.</param>
        /// <param name="outputStream">The stream to which the image data will be written.</param>
        /// <param name="fileType">The output format of the image.</param>
        /// <param name="includeAnnotations">If this is <see langword="true" />, annotations (e.g. signatures) are included in the display list that is generated. Otherwise, only the page contents are included.</param>
        public void WriteImage(int pageNumber, Rectangle region, double zoom, PixelFormats pixelFormat, Stream outputStream, RasterOutputFileTypes fileType, bool includeAnnotations = true)
        {
            if (pixelFormat == PixelFormats.RGBA && fileType == RasterOutputFileTypes.PNM)
            {
                throw new ArgumentException("Cannot save an image with alpha channel in PNM format!", nameof(fileType));
            }

            if (DisplayLists[pageNumber] == null)
            {
                DisplayLists[pageNumber] = new MuPDFDisplayList(this.OwnerContext, this.Pages[pageNumber], includeAnnotations);
            }

            if (zoom < 0.000001 | zoom * region.Width <= 0.001 || zoom * region.Height <= 0.001)
            {
                throw new ArgumentOutOfRangeException(nameof(zoom), zoom, "The zoom factor is too small!");
            }

            if (this.ImageXRes != 72 || this.ImageYRes != 72)
            {
                zoom  *= Math.Sqrt(this.ImageXRes * this.ImageYRes) / 72;
                region = new Rectangle(region.X0 * 72 / this.ImageXRes, region.Y0 * 72 / this.ImageYRes, region.X1 * 72 / this.ImageXRes, region.Y1 * 72 / this.ImageYRes);
            }

            float fzoom = (float)zoom;

            IntPtr outputBuffer     = IntPtr.Zero;
            IntPtr outputData       = IntPtr.Zero;
            ulong  outputDataLength = 0;

            ExitCodes result = (ExitCodes)NativeMethods.WriteImage(OwnerContext.NativeContext, DisplayLists[pageNumber].NativeDisplayList, region.X0, region.Y0, region.X1, region.Y1, fzoom, (int)pixelFormat, (int)fileType, ref outputBuffer, ref outputData, ref outputDataLength);

            switch (result)
            {
            case ExitCodes.EXIT_SUCCESS:
                break;

            case ExitCodes.ERR_CANNOT_RENDER:
                throw new MuPDFException("Cannot render page", result);

            case ExitCodes.ERR_CANNOT_CREATE_CONTEXT:
                throw new MuPDFException("Cannot create the output buffer", result);

            default:
                throw new MuPDFException("Unknown error", result);
            }

            byte[] buffer = new byte[1024];

            while (outputDataLength > 0)
            {
                int bytesToCopy = (int)Math.Min(buffer.Length, (long)outputDataLength);
                Marshal.Copy(outputData, buffer, 0, bytesToCopy);
                outputData = IntPtr.Add(outputData, bytesToCopy);
                outputStream.Write(buffer, 0, bytesToCopy);
                outputDataLength -= (ulong)bytesToCopy;
            }

            NativeMethods.DisposeBuffer(OwnerContext.NativeContext, outputBuffer);
        }
Example #3
0
        /// <summary>
        /// Create a new <see cref="MuPDFMultiThreadedPageRenderer"/> that renders the specified page with the specified number of threads.
        /// </summary>
        /// <param name="pageNumber">The number of the page to render (starting at 0).</param>
        /// <param name="threadCount">The number of threads to use. This must be factorisable using only powers of 2, 3, 5 or 7. Otherwise, the biggest number smaller than <paramref name="threadCount"/> that satisfies this condition is used.</param>
        /// <returns>A <see cref="MuPDFMultiThreadedPageRenderer"/> that can be used to render the specified page with the specified number of threads.</returns>
        /// <param name="includeAnnotations">If this is <see langword="true" />, annotations (e.g. signatures) are included in the display list that is generated. Otherwise, only the page contents are included.</param>
        public MuPDFMultiThreadedPageRenderer GetMultiThreadedRenderer(int pageNumber, int threadCount, bool includeAnnotations = true)
        {
            if (DisplayLists[pageNumber] == null)
            {
                DisplayLists[pageNumber] = new MuPDFDisplayList(this.OwnerContext, this.Pages[pageNumber], includeAnnotations);
            }

            return(new MuPDFMultiThreadedPageRenderer(OwnerContext, DisplayLists[pageNumber], threadCount, Pages[pageNumber].Bounds, this.ClipToPageBounds, this.ImageXRes, this.ImageYRes));
        }
Example #4
0
        /// <summary>
        /// Create a new <see cref="MuPDFMultiThreadedPageRenderer"/> from a specified display list using the specified number of threads.
        /// </summary>
        /// <param name="context">The context that owns the document from which the display list was extracted.</param>
        /// <param name="displayList">The display list to render.</param>
        /// <param name="threadCount">The number of threads to use in the rendering. This must be factorisable using only powers of 2, 3, 5 or 7. Otherwise, the biggest number smaller than <paramref name="threadCount"/> that satisfies this condition is used.</param>
        /// <param name="pageBounds">The bounds of the page being rendererd.</param>
        /// <param name="clipToPageBounds">A boolean value indicating whether the rendered image should be clipped to the original page's bounds. This can be relevant if the page has been "cropped" by altering its mediabox, but otherwise leaving the contents untouched.</param>
        /// <param name="imageXRes">If the document is an image, the horizontal resolution of the image. Otherwise, 72.</param>
        /// <param name="imageYRes">If the document is an image, the vertical resolution of the image. Otherwise, 72.</param>
        internal MuPDFMultiThreadedPageRenderer(MuPDFContext context, MuPDFDisplayList displayList, int threadCount, Rectangle pageBounds, bool clipToPageBounds, double imageXRes, double imageYRes)
        {
            threadCount = Utils.GetAcceptableNumber(threadCount);

            this.ThreadCount      = threadCount;
            this.DisplayList      = displayList;
            this.PageBounds       = pageBounds;
            this.ClipToPageBounds = clipToPageBounds;

            this.ImageXRes = imageXRes;
            this.ImageYRes = imageYRes;

            IntPtr[] contexts       = new IntPtr[threadCount];
            GCHandle contextsHandle = GCHandle.Alloc(contexts, GCHandleType.Pinned);

            try
            {
                ExitCodes result = (ExitCodes)NativeMethods.CloneContext(context.NativeContext, threadCount, contextsHandle.AddrOfPinnedObject());

                switch (result)
                {
                case ExitCodes.EXIT_SUCCESS:
                    break;

                case ExitCodes.ERR_CANNOT_INIT_MUTEX:
                    throw new MuPDFException("Cannot initalize mutex objects", result);

                case ExitCodes.ERR_CANNOT_CREATE_CONTEXT:
                    throw new MuPDFException("Cannot create master context", result);

                case ExitCodes.ERR_CANNOT_CLONE_CONTEXT:
                    throw new MuPDFException("Cannot create context clones", result);

                default:
                    throw new MuPDFException("Unknown error", result);
                }

                Contexts         = new MuPDFContext[threadCount];
                RenderingThreads = new RenderingThread[threadCount];
                for (int i = 0; i < threadCount; i++)
                {
                    Contexts[i]         = new MuPDFContext(contexts[i]);
                    RenderingThreads[i] = new RenderingThread();
                }
            }
            finally
            {
                contextsHandle.Free();
            }
        }
Example #5
0
        /// <summary>
        /// Render (part of) a page to the specified destination.
        /// </summary>
        /// <param name="pageNumber">The number of the page to render (starting at 0).</param>
        /// <param name="region">The region of the page to render in page units.</param>
        /// <param name="zoom">The scale at which the page will be rendered. This will determine the size in pixel of the image.</param>
        /// <param name="pixelFormat">The format of the pixel data.</param>
        /// <param name="destination">The address of the buffer where the pixel data will be written. There must be enough space available to write the values for all the pixels, otherwise this will fail catastrophically!</param>
        /// <param name="includeAnnotations">If this is <see langword="true" />, annotations (e.g. signatures) are included in the display list that is generated. Otherwise, only the page contents are included.</param>
        public void Render(int pageNumber, Rectangle region, double zoom, PixelFormats pixelFormat, IntPtr destination, bool includeAnnotations = true)
        {
            if (DisplayLists[pageNumber] == null)
            {
                DisplayLists[pageNumber] = new MuPDFDisplayList(this.OwnerContext, this.Pages[pageNumber], includeAnnotations);
            }

            if (zoom < 0.000001 | zoom * region.Width <= 0.001 || zoom * region.Height <= 0.001)
            {
                throw new ArgumentOutOfRangeException(nameof(zoom), zoom, "The zoom factor is too small!");
            }

            if (this.ImageXRes != 72 || this.ImageYRes != 72)
            {
                zoom  *= Math.Sqrt(this.ImageXRes * this.ImageYRes) / 72;
                region = new Rectangle(region.X0 * 72 / this.ImageXRes, region.Y0 * 72 / this.ImageYRes, region.X1 * 72 / this.ImageXRes, region.Y1 * 72 / this.ImageYRes);
            }

            float fzoom = (float)zoom;

            ExitCodes result = (ExitCodes)NativeMethods.RenderSubDisplayList(OwnerContext.NativeContext, DisplayLists[pageNumber].NativeDisplayList, region.X0, region.Y0, region.X1, region.Y1, fzoom, (int)pixelFormat, destination, IntPtr.Zero);

            switch (result)
            {
            case ExitCodes.EXIT_SUCCESS:
                break;

            case ExitCodes.ERR_CANNOT_RENDER:
                throw new MuPDFException("Cannot render page", result);

            default:
                throw new MuPDFException("Unknown error", result);
            }

            RoundedRectangle roundedRegion = region.Round(fzoom);
            RoundedSize      roundedSize   = new RoundedSize(roundedRegion.Width, roundedRegion.Height);

            if (pixelFormat == PixelFormats.RGBA || pixelFormat == PixelFormats.BGRA)
            {
                Utils.UnpremultiplyAlpha(destination, roundedSize);
            }

            if (this.ClipToPageBounds && !Pages[pageNumber].Bounds.Contains(DisplayLists[pageNumber].Bounds.Intersect(region)))
            {
                Utils.ClipImage(destination, roundedSize, region, Pages[pageNumber].Bounds, pixelFormat);
            }
        }
Example #6
0
        /// <summary>
        /// Save (part of) a page to an image file in the specified format.
        /// </summary>
        /// <param name="pageNumber">The number of the page to render (starting at 0).</param>
        /// <param name="region">The region of the page to render in page units.</param>
        /// <param name="zoom">The scale at which the page will be rendered. This will determine the size in pixel of the image.</param>
        /// <param name="pixelFormat">The format of the pixel data.</param>
        /// <param name="fileName">The path to the output file.</param>
        /// <param name="fileType">The output format of the file.</param>
        /// <param name="includeAnnotations">If this is <see langword="true" />, annotations (e.g. signatures) are included in the display list that is generated. Otherwise, only the page contents are included.</param>
        public void SaveImage(int pageNumber, Rectangle region, double zoom, PixelFormats pixelFormat, string fileName, RasterOutputFileTypes fileType, bool includeAnnotations = true)
        {
            if (pixelFormat == PixelFormats.RGBA && fileType == RasterOutputFileTypes.PNM)
            {
                throw new ArgumentException("Cannot save an image with alpha channel in PNM format!", nameof(fileType));
            }

            if (DisplayLists[pageNumber] == null)
            {
                DisplayLists[pageNumber] = new MuPDFDisplayList(this.OwnerContext, this.Pages[pageNumber], includeAnnotations);
            }

            if (zoom < 0.000001 | zoom * region.Width <= 0.001 || zoom * region.Height <= 0.001)
            {
                throw new ArgumentOutOfRangeException(nameof(zoom), zoom, "The zoom factor is too small!");
            }

            if (this.ImageXRes != 72 || this.ImageYRes != 72)
            {
                zoom  *= Math.Sqrt(this.ImageXRes * this.ImageYRes) / 72;
                region = new Rectangle(region.X0 * 72 / this.ImageXRes, region.Y0 * 72 / this.ImageYRes, region.X1 * 72 / this.ImageXRes, region.Y1 * 72 / this.ImageYRes);
            }

            float fzoom = (float)zoom;

            ExitCodes result = (ExitCodes)NativeMethods.SaveImage(OwnerContext.NativeContext, DisplayLists[pageNumber].NativeDisplayList, region.X0, region.Y0, region.X1, region.Y1, fzoom, (int)pixelFormat, fileName, (int)fileType);

            switch (result)
            {
            case ExitCodes.EXIT_SUCCESS:
                break;

            case ExitCodes.ERR_CANNOT_RENDER:
                throw new MuPDFException("Cannot render page", result);

            case ExitCodes.ERR_CANNOT_SAVE:
                throw new MuPDFException("Cannot save to the output file", result);

            default:
                throw new MuPDFException("Unknown error", result);
            }
        }