예제 #1
0
        private void uploadHorizontalPadding(ITextureUpload upload, RectangleI middleBounds, int actualPadding)
        {
            RectangleI[] sideBoundsArray =
            {
                new RectangleI(middleBounds.X - actualPadding,      middleBounds.Y, actualPadding, middleBounds.Height).Intersect(atlasBounds), // Left
                new RectangleI(middleBounds.X + middleBounds.Width, middleBounds.Y, actualPadding, middleBounds.Height).Intersect(atlasBounds), // Right
            };

            int[] sideIndices =
            {
                0,                      // Left
                middleBounds.Width - 1, // Right
            };

            for (int i = 0; i < 2; ++i)
            {
                RectangleI sideBounds = sideBoundsArray[i];

                if (!sideBounds.IsEmpty)
                {
                    bool allTransparentBlack = true;
                    int  index = sideIndices[i];

                    var sideUpload = new ArrayPoolTextureUpload(sideBounds.Width, sideBounds.Height)
                    {
                        Bounds = sideBounds
                    };

                    int stride = middleBounds.Width;

                    for (int y = 0; y < sideBounds.Height; ++y)
                    {
                        for (int x = 0; x < sideBounds.Width; ++x)
                        {
                            Rgba32 pixel = upload.Data[index + y * stride];
                            allTransparentBlack &= pixel == transparent_black;
                            sideUpload.RawData[y * sideBounds.Width + x] = pixel;
                        }
                    }

                    // Only upload padding if the border isn't completely transparent.
                    if (!allTransparentBlack)
                    {
                        // For a texture atlas, we don't care about opacity, so we avoid
                        // any computations related to it by assuming it to be mixed.
                        base.SetData(sideUpload, WrapMode.None, WrapMode.None, Opacity.Mixed);
                    }
                }
            }
        }
예제 #2
0
        private void uploadCornerPadding(ITextureUpload upload, RectangleI middleBounds, int actualPadding)
        {
            RectangleI[] cornerBoundsArray =
            {
                new RectangleI(middleBounds.X - actualPadding,      middleBounds.Y - actualPadding,       actualPadding, actualPadding).Intersect(atlasBounds), // TopLeft
                new RectangleI(middleBounds.X + middleBounds.Width, middleBounds.Y - actualPadding,       actualPadding, actualPadding).Intersect(atlasBounds), // TopRight
                new RectangleI(middleBounds.X - actualPadding,      middleBounds.Y + middleBounds.Height, actualPadding, actualPadding).Intersect(atlasBounds), // BottomLeft
                new RectangleI(middleBounds.X + middleBounds.Width, middleBounds.Y + middleBounds.Height, actualPadding, actualPadding).Intersect(atlasBounds), // BottomRight
            };

            int[] cornerIndices =
            {
                0,                                                                       // TopLeft
                middleBounds.Width - 1,                                                  // TopRight
                (middleBounds.Height - 1) * middleBounds.Width,                          // BottomLeft
                (middleBounds.Height - 1) * middleBounds.Width + middleBounds.Width - 1, // BottomRight
            };

            for (int i = 0; i < 4; ++i)
            {
                RectangleI cornerBounds  = cornerBoundsArray[i];
                int        nCornerPixels = cornerBounds.Width * cornerBounds.Height;
                Rgba32     cornerPixel   = upload.Data[cornerIndices[i]];

                // Only upload if we have a non-zero size and if the colour isn't already transparent black
                if (nCornerPixels > 0 && cornerPixel != transparent_black)
                {
                    var cornerUpload = new ArrayPoolTextureUpload(cornerBounds.Width, cornerBounds.Height)
                    {
                        Bounds = cornerBounds
                    };
                    for (int j = 0; j < nCornerPixels; ++j)
                    {
                        cornerUpload.RawData[j] = cornerPixel;
                    }

                    // For a texture atlas, we don't care about opacity, so we avoid
                    // any computations related to it by assuming it to be mixed.
                    base.SetData(cornerUpload, WrapMode.None, WrapMode.None, Opacity.Mixed);
                }
            }
        }
예제 #3
0
        private void decodingLoop(CancellationToken cancellationToken)
        {
            var packet = ffmpeg.av_packet_alloc();

            const int max_pending_frames = 3;

            try
            {
                while (true)
                {
                    if (cancellationToken.IsCancellationRequested)
                    {
                        return;
                    }

                    if (decodedFrames.Count < max_pending_frames)
                    {
                        int readFrameResult = ffmpeg.av_read_frame(formatContext, packet);

                        if (readFrameResult >= 0)
                        {
                            state = DecoderState.Running;

                            if (packet->stream_index == stream->index)
                            {
                                if (ffmpeg.avcodec_send_packet(stream->codec, packet) < 0)
                                {
                                    throw new Exception("Error sending packet.");
                                }

                                var result = ffmpeg.avcodec_receive_frame(stream->codec, frame);

                                if (result == 0)
                                {
                                    var frameTime = (frame->best_effort_timestamp - stream->start_time) * timeBaseInSeconds * 1000;

                                    if (!skipOutputUntilTime.HasValue || skipOutputUntilTime.Value < frameTime)
                                    {
                                        skipOutputUntilTime = null;

                                        SwsContext *swsCtx = null;

                                        try
                                        {
                                            swsCtx = ffmpeg.sws_getContext(codecParams.width, codecParams.height, (AVPixelFormat)frame->format, codecParams.width, codecParams.height, AVPixelFormat.AV_PIX_FMT_RGBA, 0, null, null, null);
                                            ffmpeg.sws_scale(swsCtx, frame->data, frame->linesize, 0, frame->height, ffmpegFrame->data, ffmpegFrame->linesize);
                                        }
                                        finally
                                        {
                                            ffmpeg.sws_freeContext(swsCtx);
                                        }

                                        if (!availableTextures.TryDequeue(out var tex))
                                        {
                                            tex = new Texture(codecParams.width, codecParams.height, true);
                                        }

                                        var upload = new ArrayPoolTextureUpload(tex.Width, tex.Height);

                                        // todo: can likely make this more efficient
                                        new Span <Rgba32>(ffmpegFrame->data[0], uncompressedFrameSize / 4).CopyTo(upload.RawData);

                                        tex.SetData(upload);
                                        decodedFrames.Enqueue(new DecodedFrame {
                                            Time = frameTime, Texture = tex
                                        });
                                    }

                                    lastDecodedFrameTime = (float)frameTime;
                                }
                            }
                        }
                        else if (readFrameResult == ffmpeg.AVERROR_EOF)
                        {
                            if (Looping)
                            {
                                Seek(0);
                            }
                            else
                            {
                                state = DecoderState.EndOfStream;
                            }
                        }
                        else
                        {
                            state = DecoderState.Ready;
                            Thread.Sleep(1);
                        }
                    }
                    else
                    {
                        // wait until existing buffers are consumed.
                        state = DecoderState.Ready;
                        Thread.Sleep(1);
                    }

                    while (!decoderCommands.IsEmpty)
                    {
                        if (cancellationToken.IsCancellationRequested)
                        {
                            return;
                        }

                        if (decoderCommands.TryDequeue(out var cmd))
                        {
                            cmd();
                        }
                    }
                }
            }
            catch (Exception)
            {
                state = DecoderState.Faulted;
            }
            finally
            {
                ffmpeg.av_packet_free(&packet);

                if (state != DecoderState.Faulted)
                {
                    state = DecoderState.Stopped;
                }
            }
        }