예제 #1
0
        public void Initialize()
        {
            // *** Perform basic init.  Set up the bitmap, cache stride, etc

            _RenderableChunks = _Config.RenderSubregion ? 0 : _Metrics.NumberOfChunks;

            try
            {
                _OutputMap     = new Bitmap((_Config.SubregionChunks.Width + 1) * 16, (_Config.SubregionChunks.Height + 1) * 16, PixelFormat.Format32bppArgb);
                _PendingRender = true;
            }
            catch (OutOfMemoryException)
            {
                RenderingErrorEventArgs E = new RenderingErrorEventArgs
                {
                    ErrorCode        = ErrorNoMemory,
                    IsFatal          = true,
                    UserErrorMessage =
                        "Failed to allocate dimension bitmap: memory unavailable.  Try rendering a smaller area"
                };

                if (RenderError != null)
                {
                    RenderError.Invoke(this, E);
                }
            }
            catch (Exception Ex)
            {
                RenderingErrorEventArgs E = new RenderingErrorEventArgs
                {
                    ErrorException     = Ex,
                    ErrorCode          = ErrorNoMemory,
                    ShowInnerException = true,
                    IsFatal            = true,
                    UserErrorMessage   = "Failed to allocate dimension bitmap."
                };

                if (RenderError != null)
                {
                    RenderError.Invoke(this, E);
                }
            }

            _RenderTarget = _OutputMap.LockBits(new Rectangle(0, 0, _OutputMap.Width, _OutputMap.Height), ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb);
            _Stride       = _RenderTarget.Stride;
        }
예제 #2
0
 /// <summary>
 /// Propagate the Render Error event from root container.
 /// </summary>
 protected virtual void OnRenderError(HtmlRenderErrorEventArgs e)
 {
     RenderError?.Invoke(this, e);
 }
예제 #3
0
        // ReSharper disable once FunctionComplexityOverflow
        // *** I do ^ because this function is the 'hottest' block of code in the program, and saving cycles during execution is ridiculously important here.
        void RenderChunk(ChunkRef Chunk, ParallelLoopState LoopState)
        {
            // *** Track how many chunks have been processed, for user feedback
            Interlocked.Increment(ref _ProcessedChunks);
            Interlocked.Increment(ref _ActiveRenderThreads);
#if !DEBUG && !FAST
            // *** In release mode, gracefully handle bad chunks.  Explode in debug mode so I can track down the issue.
            try
            {
#endif

            int[][] DepthOpacities = _ColourPalette.DepthOpacities;

            // *** Cancellation logic for parallel processing
            if (LoopState != null && _Cancellation.IsCancellationRequested)
            {
                LoopState.Stop();
            }

            if (LoopState != null && LoopState.IsStopped)
            {
                Interlocked.Decrement(ref _ActiveRenderThreads);
                return;
            }

            // *** Hold off on rendering if the user needs to attend to an issue
            while (_PauseRendering > 0)
            {
                Thread.Sleep(50);
            }

            // *** Load the chunk from disk here
            AlphaBlockCollection Blocks = Chunk.Blocks;

            for (int X = 0; X < 16; X++)
            {
                for (int Z = 0; Z < 16; Z++)
                {
                    // *** Start by finding the topmost block to render
                    int EndY = _RenderStartY(Blocks, X, Z);
                    int Y    = EndY;

                    if (Y < 0)
                    {
                        continue; // *** No valid renderable blocks in this column, so continue with the next column
                    }
                    // *** Drill into the column to determine how many blocks down to render
                    int RenderVal = 255;
                    while (RenderVal > 0)
                    {
                        RenderVal -= DepthOpacities[Blocks.GetID(X, Y, Z)][Blocks.GetData(X, Y, Z)];
                        if (Y == 0) // *** If we've hit the bottom of the map, don't try and keep going.
                        {
                            break;  // *** It wouldn't end well.
                        }
                        Y--;
                    }

                    Colour SetColour = Colour.Transparent; // *** What colour to set the current column's pixel to.

                    // *** The Block-Metadata palette for this column's biome
                    Colour[][] BiomePalette = _ColourPalette.FastPalette[Chunk.Biomes.GetBiome(X, Z)];

                    for (; Y <= EndY; Y++) // *** Now render up from the lowest block to the starting block
                    {
                        // *** For each block we render, grab its palette entry.
                        Colour Entry = BiomePalette[Blocks.GetID(X, Y, Z)][Blocks.GetData(X, Y, Z)];

                        // *** If it has an associated entity colours list, then it needs special consideration to get its colour
                        if ((Entry.Color & 0xFFFF0000U) == 0x00FF0000U) // *** Check for the flag value (0 Alpha, 255 Red - Blue and Green form the 0-65535 index)
                        {
                            PaletteEntry Entry2 = _ColourPalette.GetPaletteEntry((int)(Entry.Color & 0x0000FFFFU)).First(e => e.IsMatch(Blocks.GetData(X, Y, Z), Blocks.SafeGetTileEntity(X, Y, Z)));
                            if (Entry2 != null)
                            {
                                Entry = Entry2.Color;
                            }
                        }

                        if (Entry.A == 0)
                        {
                            continue; // *** If we're trying to render air, let's not.
                        }
                        // *** Blend in our working colour to the column's pixel, after applying altitude and light-level blends.
                        SetColour.Blend(Entry.Copy().LightLevel((uint)Math.Max(_Config.MinLightLevel, Blocks.GetBlockLight(X, Math.Min(Y + 1, 255), Z))).Altitude(Y));
                    }

                    Marshal.WriteInt32(_RenderTarget.Scan0 + (_Stride * (((Chunk.Z - _Config.SubregionChunks.Y) << 4) + Z)) + ((((Chunk.X - _Config.SubregionChunks.X) << 4) + X) << 2), (int)SetColour.FullAlpha().Color);
                }
            }
#if !DEBUG && !FAST // *** When not running in debug mode, chunks that fail to render should NOT crash everything.
        }

        catch (Exception Ex)
        {
            Interlocked.Increment(ref _PauseRendering);

            _CorruptChunks = true;
            RenderingErrorEventArgs E = new RenderingErrorEventArgs
            {
                ErrorException   = Ex,
                IsFatal          = false,
                UserErrorMessage = "A chunk failed to render",
                ErrorCode        = ErrorBadChunk
            };

            if (RenderError != null)
            {
                RenderError.Invoke(this, E);
            }

            Interlocked.Decrement(ref _PauseRendering);
        }
#endif
            Interlocked.Decrement(ref _ActiveRenderThreads);
        }
예제 #4
0
        // *** Rendering Functions
        public void Render()
        {
            // *** Define the chunk query, in order to cleanly support subregions
            IEnumerable <ChunkRef> ChunkProvider = from ChunkRef Chunk in _Chunks
                                                   where !_Config.RenderSubregion || _Config.SubregionChunks.ContainsPoint(Chunk.X, Chunk.Z)
                                                   select Chunk;

            _PendingRender = false;

            // *** If rendering a subregion, then wipe the _RenderableChunks var and recalculate it from the number of chunks in the subregion, not the world
            if (_Config.RenderSubregion)
            {
                _RenderableChunks = ChunkProvider.Count();
            }

            // *** If rendering was cancelled between init and now, abort.
            if (_Cancellation.IsCancellationRequested)
            {
                _OutputMap.Dispose();
                _OutputMap = null;
                return;
            }

            // *** Render
            if (_Config.EnableMultithreading)
            {
                Thread UpdateThread = new Thread(DisplayProgress);
                UpdateThread.Start();

                _RenderingParallelOptions.CancellationToken      = _Cancellation.Token;
                _RenderingParallelOptions.MaxDegreeOfParallelism = _Config.MaxThreads;

                try
                {
                    Parallel.ForEach(ChunkProvider, _RenderingParallelOptions, RenderChunk);
                    UpdateThread.Join();
                }
                catch (OperationCanceledException)
                {
                    // Discard exception - this just means the user cancelled.
                }
            }
            else
            {
                foreach (ChunkRef Chunk in ChunkProvider)
                {
                    RenderChunk(Chunk, null);
                    if (_Cancellation.IsCancellationRequested)
                    {
                        break;
                    }
                    DoProgressUpdate();
                }
            }

            _OutputMap.UnlockBits(_RenderTarget);

            if (_Cancellation.IsCancellationRequested)
            {
                _OutputMap.Dispose();
                _OutputMap = null;
                return;
            }

            // *** If this is not a preview, then make sure the output directory exists then save the output bitmap
            if (!_Config.IsPreview)
            {
                // ReSharper disable once AssignNullToNotNullAttribute
                Directory.CreateDirectory(Path.GetDirectoryName(_Config.SaveFilename));
                _OutputMap.Save(_Config.SaveFilename);
                _OutputMap.Dispose();
                _OutputMap = null;
            }

            // *** If a chunk failed to render, let the user know.  Unless we aborted, because there's no point then.
            if (_CorruptChunks)
            {
                RenderingErrorEventArgs E = new RenderingErrorEventArgs
                {
                    ErrorCode        = ErrorErrors,
                    IsFatal          = true,
                    UserErrorMessage =
                        "WARNING: At least one potentially corrupt chunk was encountered during rendering.\r\nThe map image may contain missing sections as a result."
                };

                if (RenderError != null)
                {
                    RenderError.Invoke(this, E);
                }
            }
        }