Example #1
0
        /// <summary>
        /// Performs the actual rendering.
        /// </summary>
        private void RenderAction()
        {
            ExitCodes result = (ExitCodes)NativeMethods.RenderSubDisplayList(this.CurrentRenderData.Context, this.CurrentRenderData.DisplayList.NativeDisplayList, this.CurrentRenderData.Region.X0, this.CurrentRenderData.Region.Y0, this.CurrentRenderData.Region.X1, this.CurrentRenderData.Region.Y1, this.CurrentRenderData.Zoom, (int)this.CurrentRenderData.PixelFormat, this.CurrentRenderData.PixelStorage, Cookie);

            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 = this.CurrentRenderData.Region.Round(this.CurrentRenderData.Zoom);
            RoundedSize      roundedSize   = new RoundedSize(roundedRegion.Width, roundedRegion.Height);

            if (this.CurrentRenderData.PixelFormat == PixelFormats.RGBA || this.CurrentRenderData.PixelFormat == PixelFormats.BGRA)
            {
                Utils.UnpremultiplyAlpha(this.CurrentRenderData.PixelStorage, roundedSize);
            }

            if (this.CurrentRenderData.ClipToPageBounds && !this.CurrentRenderData.PageBounds.Contains(this.CurrentRenderData.DisplayList.Bounds.Intersect(this.CurrentRenderData.Region)))
            {
                Utils.ClipImage(this.CurrentRenderData.PixelStorage, roundedSize, this.CurrentRenderData.Region, this.CurrentRenderData.PageBounds, this.CurrentRenderData.PixelFormat);
            }
        }
Example #2
0
        /// <summary>
        /// Split the rectangle into the specified number of <see cref="RoundedRectangle"/>s.
        /// </summary>
        /// <param name="divisions">The number of rectangles in which the rectangle should be split. This must be factorisable using only powers of 2, 3, 5 or 7. Otherwise, the biggest number smaller than <paramref name="divisions"/> that satisfies this condition is used.</param>
        /// <returns>An array of <see cref="RoundedRectangle"/>s that when positioned properly cover the same area as this object.</returns>
        public     RoundedRectangle[] Split(int divisions)
        {
            RoundedSize s = new RoundedSize(this.Width, this.Height);

            RoundedRectangle[] splitSize = s.Split(divisions);

            RoundedRectangle[] tbr = new RoundedRectangle[divisions];

            for (int i = 0; i < splitSize.Length; i++)
            {
                tbr[i] = new RoundedRectangle(this.X0 + splitSize[i].X0, this.Y0 + splitSize[i].Y0, this.X0 + splitSize[i].X1, this.Y0 + splitSize[i].Y1);
            }

            return(tbr);
        }
Example #3
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 #4
0
        /// <summary>
        /// Converts an image with premultiplied alpha values into an image with unpremultiplied alpha values.
        /// </summary>
        /// <param name="image">A pointer to the address where the pixel data is stored.</param>
        /// <param name="imageSize">The size in pixels of the image.</param>
        public static void UnpremultiplyAlpha(IntPtr image, RoundedSize imageSize)
        {
            int stride = imageSize.Width * 4;

            unsafe
            {
                byte *imageData = (byte *)image;

                for (int y = 0; y < imageSize.Height; y++)
                {
                    for (int x = 0; x < imageSize.Width; x++)
                    {
                        if (imageData[y * stride + x * 4 + 3] > 0)
                        {
                            imageData[y * stride + x * 4]     = (byte)(imageData[y * stride + x * 4] * 255 / imageData[y * stride + x * 4 + 3]);
                            imageData[y * stride + x * 4 + 1] = (byte)(imageData[y * stride + x * 4 + 1] * 255 / imageData[y * stride + x * 4 + 3]);
                            imageData[y * stride + x * 4 + 2] = (byte)(imageData[y * stride + x * 4 + 2] * 255 / imageData[y * stride + x * 4 + 3]);
                        }
                    }
                }
            }
        }
Example #5
0
        /// <summary>
        /// Render the specified region to an image of the specified size, split in a number of tiles equal to the number of threads used by this <see cref="MuPDFMultiThreadedPageRenderer"/>, without marshaling. This method will not return until all the rendering threads have finished.
        /// </summary>
        /// <param name="targetSize">The total size of the image that should be rendered.</param>
        /// <param name="region">The region in page units that should be rendered.</param>
        /// <param name="destinations">An array containing the addresses of the buffers where the rendered tiles will be written. There must be enough space available in each buffer to write the values for all the pixels of the tile, otherwise this will fail catastrophically!
        /// As long as the <paramref name="targetSize"/> is the same, the size in pixel of the tiles is guaranteed to also be the same.</param>
        /// <param name="pixelFormat">The format of the pixel data.</param>
        public void Render(RoundedSize targetSize, Rectangle region, IntPtr[] destinations, PixelFormats pixelFormat)
        {
            if (destinations.Length != Contexts.Length)
            {
                throw new ArgumentOutOfRangeException(nameof(destinations), destinations.Length, "The number of destinations must be equal to the number of rendering threads!");
            }

            RoundedRectangle[] targets = targetSize.Split(destinations.Length);

            float zoomX = targetSize.Width / region.Width;
            float zoomY = targetSize.Height / region.Height;

            float zoom = (float)Math.Sqrt(zoomX * zoomY);

            Rectangle actualPageArea = new Rectangle(region.X0, region.Y0, region.X0 + targetSize.Width / zoom, region.Y0 + targetSize.Height / zoom);

            Rectangle[] origins = actualPageArea.Split(destinations.Length);

            //Make sure that each tile has the expected size in pixel, rounding errors notwithstanding.
            for (int i = 0; i < origins.Length; i++)
            {
                int countBlanks = 0;
                RoundedRectangle roundedOrigin = origins[i].Round(zoom);
                while (roundedOrigin.Width != targets[i].Width || roundedOrigin.Height != targets[i].Height)
                {
                    RoundedRectangle oldRoundedOrigin = roundedOrigin;

                    if (roundedOrigin.Width > targets[i].Width)
                    {
                        if (origins[i].X0 > actualPageArea.X0)
                        {
                            origins[i] = new Rectangle(origins[i].X0 + 0.5 / zoom, origins[i].Y0, origins[i].X1, origins[i].Y1);
                        }
                        else
                        {
                            origins[i] = new Rectangle(origins[i].X0, origins[i].Y0, origins[i].X1 - 0.5 / zoom, origins[i].Y1);
                        }
                    }
                    else if (roundedOrigin.Width < targets[i].Width)
                    {
                        if (origins[i].X1 < actualPageArea.X1)
                        {
                            origins[i] = new Rectangle(origins[i].X0, origins[i].Y0, origins[i].X1 + 0.5 / zoom, origins[i].Y1);
                        }
                        else
                        {
                            origins[i] = new Rectangle(origins[i].X0 - 0.5 / zoom, origins[i].Y0, origins[i].X1, origins[i].Y1);
                        }
                    }


                    if (roundedOrigin.Height > targets[i].Height)
                    {
                        if (origins[i].Y0 > actualPageArea.Y0)
                        {
                            origins[i] = new Rectangle(origins[i].X0, origins[i].Y0 + 0.5 / zoom, origins[i].X1, origins[i].Y1);
                        }
                        else
                        {
                            origins[i] = new Rectangle(origins[i].X0, origins[i].Y0, origins[i].X1, origins[i].Y1 - 0.5 / zoom);
                        }
                    }
                    else if (roundedOrigin.Height < targets[i].Height)
                    {
                        if (origins[i].X1 < actualPageArea.X1)
                        {
                            origins[i] = new Rectangle(origins[i].X0, origins[i].Y0, origins[i].X1, origins[i].Y1 + 0.5 / zoom);
                        }
                        else
                        {
                            origins[i] = new Rectangle(origins[i].X0, origins[i].Y0 - 0.5 / zoom, origins[i].X1, origins[i].Y1);
                        }
                    }

                    roundedOrigin = origins[i].Round(zoom);

                    if (roundedOrigin.Width == oldRoundedOrigin.Width && roundedOrigin.Height == oldRoundedOrigin.Height && (roundedOrigin.Width != targets[i].Width || roundedOrigin.Height != targets[i].Height))
                    {
                        countBlanks++;
                    }

                    if (countBlanks >= 100)
                    {
                        //It seems that we can't coerce the expected size and the actual size to be the same. Give up.
                        return;
                    }
                }
            }

            //Start each rendering thread.
            for (int i = 0; i < destinations.Length; i++)
            {
                double    dzoom  = zoom;
                Rectangle origin = origins[i];
                if (this.ImageXRes != 72 || this.ImageYRes != 72)
                {
                    dzoom *= Math.Sqrt(this.ImageXRes * this.ImageYRes) / 72;
                    origin = new Rectangle(origin.X0 * 72 / this.ImageXRes, origin.Y0 * 72 / this.ImageYRes, origin.X1 * 72 / this.ImageXRes, origin.Y1 * 72 / this.ImageYRes);
                }

                RenderingThreads[i].Render(Contexts[i].NativeContext, DisplayList, origin, (float)dzoom, destinations[i], pixelFormat, this.PageBounds, ClipToPageBounds);
            }

            //Wait until all the rendering threads have finished.
            for (int i = 0; i < destinations.Length; i++)
            {
                RenderingThreads[i].WaitForRendering();
            }
        }
Example #6
0
        /// <summary>
        /// Clear all pixels outside of a specified region.
        /// </summary>
        /// <param name="image">A pointer to the address where the pixel data is stored.</param>
        /// <param name="imageSize">The size in pixels of the image.</param>
        /// <param name="imageArea">The area represented by the image.</param>
        /// <param name="clipArea">The region outside which all pixels will be cleared, in image units.</param>
        /// <param name="pixelFormat">The format of the pixel data.</param>
        public static void ClipImage(IntPtr image, RoundedSize imageSize, Rectangle imageArea, Rectangle clipArea, PixelFormats pixelFormat)
        {
            int clipLeft  = Math.Max(0, (int)Math.Ceiling((clipArea.X0 - imageArea.X0) / imageArea.Width * imageSize.Width - 0.001));
            int clipRight = Math.Max(0, (int)Math.Floor(imageSize.Width - (imageArea.X1 - clipArea.X1) / imageArea.Width * imageSize.Width + 0.001));

            int clipTop    = Math.Max(0, (int)Math.Ceiling((clipArea.Y0 - imageArea.Y0) / imageArea.Height * imageSize.Height - 0.001));
            int clipBottom = Math.Max(0, (int)Math.Floor(imageSize.Height - (imageArea.Y1 - clipArea.Y1) / imageArea.Height * imageSize.Height + 0.001));

            int  pixelSize  = -1;
            byte clearValue = 0;

            switch (pixelFormat)
            {
            case PixelFormats.RGB:
            case PixelFormats.BGR:
                pixelSize  = 3;
                clearValue = 255;
                break;

            case PixelFormats.RGBA:
            case PixelFormats.BGRA:
                pixelSize  = 4;
                clearValue = 0;
                break;
            }

            int stride = imageSize.Width * pixelSize;

            if (clipLeft > 0 || clipRight < imageSize.Width || clipTop > 0 || clipBottom < imageSize.Height)
            {
                unsafe
                {
                    byte *imageData = (byte *)image;

                    for (int y = 0; y < imageSize.Height; y++)
                    {
                        if (y < clipTop || y >= clipBottom)
                        {
                            for (int x = 0; x < imageSize.Width; x++)
                            {
                                for (int i = 0; i < pixelSize; i++)
                                {
                                    imageData[y * stride + x * pixelSize + i] = clearValue;
                                }
                            }
                        }
                        else
                        {
                            for (int x = 0; x < Math.Min(clipLeft, imageSize.Width); x++)
                            {
                                for (int i = 0; i < pixelSize; i++)
                                {
                                    imageData[y * stride + x * pixelSize + i] = clearValue;
                                }
                            }

                            for (int x = Math.Max(0, clipRight); x < imageSize.Width; x++)
                            {
                                for (int i = 0; i < pixelSize; i++)
                                {
                                    imageData[y * stride + x * pixelSize + i] = clearValue;
                                }
                            }
                        }
                    }
                }
            }
        }
Example #7
0
        /// <summary>
        /// Split the size into the specified number of <see cref="RoundedRectangle"/>s.
        /// </summary>
        /// <param name="divisions">The number of rectangles in which the size should be split. This must be factorisable using only powers of 2, 3, 5 or 7. Otherwise, the biggest number smaller than <paramref name="divisions"/> that satisfies this condition is used.</param>
        /// <returns>An array of <see cref="RoundedRectangle"/>s that when positioned properly cover an area of the size of this object.</returns>
        public     RoundedRectangle[] Split(int divisions)
        {
            divisions = Utils.GetAcceptableNumber(divisions);

            RoundedRectangle[] tbr = new RoundedRectangle[divisions];

            bool isVertical = this.Height > this.Width;

            if (divisions == 1)
            {
                tbr[0] = new RoundedRectangle(0, 0, Width, Height);
            }
            else if (divisions == 2)
            {
                if (isVertical)
                {
                    tbr[0] = new RoundedRectangle(0, 0, Width, Height / 2);
                    tbr[1] = new RoundedRectangle(0, Height / 2, Width, Height);
                }
                else
                {
                    tbr[0] = new RoundedRectangle(0, 0, Width / 2, Height);
                    tbr[1] = new RoundedRectangle(Width / 2, 0, Width, Height);
                }
            }
            else if (divisions == 3)
            {
                if (isVertical)
                {
                    tbr[0] = new RoundedRectangle(0, 0, Width / 2, 2 * Height / 3);
                    tbr[1] = new RoundedRectangle(Width / 2, 0, Width, 2 * Height / 3);
                    tbr[2] = new RoundedRectangle(0, 2 * Height / 3, Width, Height);
                }
                else
                {
                    tbr[0] = new RoundedRectangle(0, 0, 2 * Width / 3, Height / 2);
                    tbr[1] = new RoundedRectangle(0, Height / 2, 2 * Width / 3, Height);
                    tbr[2] = new RoundedRectangle(2 * Width / 3, 0, Width, Height);
                }
            }
            else if (divisions == 5)
            {
                if (isVertical)
                {
                    tbr[0] = new RoundedRectangle(0, 0, Width / 2, 2 * Height / 5);
                    tbr[1] = new RoundedRectangle(Width / 2, 0, Width, 2 * Height / 5);
                    tbr[2] = new RoundedRectangle(0, 2 * Height / 5, Width / 2, 4 * Height / 5);
                    tbr[3] = new RoundedRectangle(Width / 2, 2 * Height / 5, Width, 4 * Height / 5);
                    tbr[4] = new RoundedRectangle(0, 4 * Height / 5, Width, Height);
                }
                else
                {
                    tbr[0] = new RoundedRectangle(0, 0, 2 * Width / 5, Height / 2);
                    tbr[1] = new RoundedRectangle(0, Height / 2, 2 * Width / 5, Height);
                    tbr[2] = new RoundedRectangle(2 * Width / 5, 0, 4 * Width / 5, Height / 2);
                    tbr[3] = new RoundedRectangle(2 * Width / 5, Height / 2, 4 * Width / 5, Height);
                    tbr[4] = new RoundedRectangle(4 * Width / 5, 0, Width, Height);
                }
            }
            else if (divisions == 7)
            {
                if (isVertical)
                {
                    tbr[0] = new RoundedRectangle(0, 0, Width / 2, 2 * Height / 7);
                    tbr[1] = new RoundedRectangle(Width / 2, 0, Width, 2 * Height / 7);
                    tbr[2] = new RoundedRectangle(0, 2 * Height / 7, Width / 2, 4 * Height / 7);
                    tbr[3] = new RoundedRectangle(Width / 2, 2 * Height / 7, Width, 4 * Height / 7);
                    tbr[4] = new RoundedRectangle(0, 4 * Height / 7, Width / 2, 6 * Height / 7);
                    tbr[5] = new RoundedRectangle(Width / 2, 4 * Height / 7, Width, 6 * Height / 7);
                    tbr[6] = new RoundedRectangle(0, 6 * Height / 7, Width, Height);
                }
                else
                {
                    tbr[0] = new RoundedRectangle(0, 0, 2 * Width / 7, Height / 2);
                    tbr[1] = new RoundedRectangle(0, Height / 2, 2 * Width / 7, Height);
                    tbr[2] = new RoundedRectangle(2 * Width / 7, 0, 4 * Width / 7, Height / 2);
                    tbr[3] = new RoundedRectangle(2 * Width / 7, Height / 2, 4 * Width / 7, Height);
                    tbr[4] = new RoundedRectangle(4 * Width / 7, 0, 6 * Width / 7, Height / 2);
                    tbr[5] = new RoundedRectangle(4 * Width / 7, Height / 2, 6 * Width / 7, Height);
                    tbr[6] = new RoundedRectangle(6 * Width / 7, 0, Width, Height);
                }
            }
            else
            {
                for (int divisorInd = 0; divisorInd < Utils.AcceptableDivisors.Length; divisorInd++)
                {
                    if (divisions % Utils.AcceptableDivisors[divisorInd] == 0)
                    {
                        RoundedRectangle[] largerDivisions = this.Split(divisions / Utils.AcceptableDivisors[divisorInd]);

                        int pos = 0;

                        for (int i = 0; i < largerDivisions.Length; i++)
                        {
                            RoundedSize        s            = new RoundedSize(largerDivisions[i].Width, largerDivisions[i].Height);
                            RoundedRectangle[] currDivision = s.Split(Utils.AcceptableDivisors[divisorInd]);

                            for (int j = 0; j < currDivision.Length; j++)
                            {
                                tbr[pos] = new RoundedRectangle(largerDivisions[i].X0 + currDivision[j].X0, largerDivisions[i].Y0 + currDivision[j].Y0, largerDivisions[i].X0 + currDivision[j].X1, largerDivisions[i].Y0 + currDivision[j].Y1);
                                pos++;
                            }
                        }

                        break;
                    }
                }
            }

            return(tbr);
        }