настраиваемая часть изображения .net 4.5

Я произвольно рисую на экране два увеличенных изображения, одно рядом друг с другом. Каждый из них занимает половину экрана.

Я ранее делал это в .net 3.5 (я думаю), переопределив OnPaint ():

    //using System.Drawing

    /// <summary>
    /// Custom drawing
    /// </summary>
    /// <param name="e"></param>
    protected override void OnPaint(PaintEventArgs e)
    {
        e.Graphics.DrawImage(Image, DestRectangle, SrcRectangle, GraphicsUnit);
    }

Описание метода DrawImage: «Рисует указанную часть указанного изображения в указанном месте и с указанным размером». (MSDN)

Я пытаюсь добиться того же, используя .net 4.5. Я переопределяю OnRender и использую объект DrawingContext для выполнения своих рисунков. В основном это мой цикл:

    //using System.Windows.Media;

    /// <summary>
    /// Overide the OnRender to have access to a lower level of drawing.
    /// </summary>
    /// <param name="drawingContext"></param>
    protected override void OnRender(DrawingContext drawingContext)
    {
        drawingContext.DrawImage(BitmapImage_Left, Window_LeftHalf);
        drawingContext.DrawImage(BitmapImage_Right, Window_RightHalf);
    }

Он отлично работает, если я хочу отобразить растянутую картинку. Я хочу отобразить (в Window_LeftHalf и Window_RightHalf) часть изображения (например, увеличение масштаба). В основном то, что делает graphics.DrawImage (см. Выше), но с использованием объекта DrawingContext.

Я попытался посмотреть MSDN, но ничего интересного не нашел. Может быть, создать буфер, который позже будет использоваться контекстом рисования? Я почти уверен, что нужен промежуточный объект, содержащий увеличенные изображения. Любые идеи?

ОБНОВЛЕНИЕ. Я использую мышь для навигации по изображению, поэтому производительность важна. Например:

    /// <summary>
    /// Handles the mouse move events.
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    private static void MouseMoveEventHandler(RoutedEventArgs e)
    {
        // The size of the crop is always the same
        // but the portion of the picture different.
        crop.X += mouseDelta.X;
        crop.Y += mouseDelta.Y;
    }

person jimasun    schedule 30.11.2012    source источник


Ответы (2)


Взгляните на класс CroppedBitmap. Как и в случае с e.Graphics.DrawImage(), CroppedBitmap позволяет указать только ту часть изображения, которая вас интересует.

Вот пример:

protected override void OnRender(System.Windows.Media.DrawingContext dc)
{
    int halfWidth = (int)this.Width / 2;
    int height = (int)this.Height;
    BitmapImage leftImage = new BitmapImage(new Uri(@"C:\Users\Public\Pictures\Sample Pictures\Chrysanthemum.jpg"));
    BitmapImage rightImage = new BitmapImage(new Uri(@"C:\Users\Public\Pictures\Sample Pictures\Desert.jpg"));
    CroppedBitmap leftImageCropped = new CroppedBitmap(leftImage, new Int32Rect(0, 0, halfWidth, height));
    CroppedBitmap rightImageCropped = new CroppedBitmap(rightImage, new Int32Rect(0, 0, halfWidth, height));
    dc.DrawImage(leftImageCropped, new System.Windows.Rect(0, 0, leftImageCropped.Width, height));
    dc.DrawImage(rightImageCropped, new System.Windows.Rect(halfWidth, 0, halfWidth, height));
}
person cokeman19    schedule 01.12.2012
comment
Привет, cokeman19, благодарю за ответ. Да! Я еще не пробовал, но он выглядит именно так, как я хочу. Единственное отличие состоит в том, что каждый раз, когда мне нужен новый раздел изображения, мне нужно создавать / изменять объект CroppedBitmap, где e.Graphics.DrawImage() будет все время использовать одно и то же растровое изображение. В любом случае спасибо, я воспользуюсь им. Я обновлю свой пост по более конкретному вопросу. - person jimasun; 03.12.2012

ИЗМЕНИТЬ 2: ImageBrush.Viewbox. Окно просмотра - это Rect с размерами [0,0 ... 1,0], которые позволяют вам управлять тем, что раньше было SourceRect. Я протестировал это, и он отлично работает. Что я сделал:

В моем окне:

    protected ImageBrush imgBrush = new ImageBrush(new ImageSource(new Uri("image.png")));
    protected Rect vBox = new Rect(0, 0, 1, 1);
    protected Point lastPosition = new Point(0, 0);

Моим контейнером для этого был прямоугольник WPF с именем tgtRect, Rect.Fill которого был imgBrush. Методы масштабирования и прокрутки были следующими:

    protected void tgtRect_MouseWheel(object sender, MouseWheelEventArgs e)
    {
        // Zoom in when Delta is positive, Zoom out when negative
        double exp = -e.Delta / Math.Abs(e.Delta);
        double val = Math.Pow(1.1, exp);
        vBox.Scale(val, val);
        imgBrush.Viewbox = vBox;
    }

    void tgtRect_MouseMove(object sender, MouseEventArgs e)
    {
        Point thisPosition = e.GetPosition(tgtRect);
        if (e.RightButton == MouseButtonState.Pressed)
        {
            double w = tgtRect.ActualWidth;
            double h = tgtRect.ActualHeight;
            Vector offset = lastPosition - thisPosition;
            offset.X /= w;
            offset.Y /= h;
            vBox.Offset(offset);
            imgBrush.Viewbox = vBox;
        }
        lastPosition = thisPosition;
    }

Для вашей реализации:

    protected override void OnRender(DrawingContext drawingContext)
    {
        drawingContext.DrawRectangle(imgBrush, null, DesRect);
    }

Скорее всего, вам понадобится создать отдельный imgBrush для каждого прямоугольника, который вы хотите нарисовать. Я попробовал приведенный выше код (не переопределение OnRender) в окне WPF только с прямоугольником, Rectangle.Fill которого было этим ImageBrush, и производительность была очень хорошей. Если у вас возникнут проблемы, я думаю, мы сможем с этим разобраться, но я думаю, что ImageBrush окажется правильной реализацией. Это был очень интересный проект! Спасибо Вам за Ваш вопрос.

КОНЕЦ РЕДАКТИРОВАНИЯ 2

Вам нужно будет определить Rect объекты «Window_LeftHalf» и «Window_RightHalf», чтобы они имели фактический размер, который вы хотите, чтобы изображения отображались. Например, если вы увеличили масштаб на 200%, тогда свойства Rect.Width и Rect.Height должны быть в 2 раза больше размера исходного ImageSource.

ИЗМЕНИТЬ:

Старый метод был:

protected override void OnPaint(PaintEventArgs e)
{
    e.Graphics.DrawImage(Image, DestRectangle, SrcRectangle, GraphicsUnit);
}

Использование BitmapImage.SourceRect:

protected override void OnRender(DrawingContext drawingContext)
{
    BitmapImage_Left.SourceRect = SrcRectangleLeft;
    drawingContext.DrawImage(BitmapImage_Left, Window_LeftHalf);
    BitmapImage_Right.SourceRect = SrcRectangleRight;
    drawingContext.DrawImage(BitmapImage_Right, Window_RightHalf);
}

Функции вашей мыши могут изменять SourceRect. Например:

private static void MouseMoveEventHandler(RoutedEventArgs e)
{
    // The size of the crop is always the same
    // but the portion of the picture different.
    SrcRectangleLeft = new Int32Rect(SrcRectangleLeft.X + mouseDelta.X, 
        SrcRectangleLeft.Y + mouseDelta.Y,
        SrcRectangleLeft.Width, SrcRectangleLeft.Height);
}

Не уверен, какой будет производительность, но она должна быть лучше, чем отображение частей растрового изображения на новые объекты при каждом обновлении.

Надеюсь, это вообще помогло.

person newb    schedule 01.12.2012
comment
Привет, новичок, спасибо за ответ. Я пробовал это раньше. Если я это сделаю, изображения будут перекрываться. Даже если я покажу их в нужном месте, Window_ Half не будет отображать * определенные части изображений (увеличенные). - person jimasun; 03.12.2012
comment
Это имело бы смысл, если бы вы не перемещали целевые прямоугольники. Я перечитываю это и представляю себе окна с параллельной прокруткой для каждой половины окна. Это правильно? - person newb; 03.12.2012
comment
Да, бок о бок, фиксированные окна. Но содержание всегда меняется. Поэтому, если я отображаю 200%, я могу иметь только фиксированный контент (по оси x). - person jimasun; 04.12.2012
comment
В порядке. Думаю, у меня есть другое решение. Если ваши BitmapImage_Left и BitmapImage_Right действительно являются объектами BitmapImage, вы можете установить их свойство BitmapImage.SourceRect на основе положения и масштабирования. Я отредактирую исходный ответ примером. - person newb; 04.12.2012
comment
Если SourceRect - это область изображения, которую следует учитывать, то я думаю, что это ответ. Не знаю, как я это полностью упустил. Я еще не проверял это, но обязательно вернусь сюда, если это не сработает. Спасибо! - person jimasun; 06.12.2012
comment
Хорошо, здесь есть небольшая проблема. Свойство BitmapImage.SourceRect не может быть изменено по запросу. Или, по крайней мере, это то, что я понял (MSDN Раздел «Замечания»). Если есть способ, то это действительное решение, в противном случае я буду придерживаться CroppedBitmap. - person jimasun; 06.12.2012
comment
Я просто просматривал решение, в котором мне недавно пришлось сделать что-то подобное (обрезка изображений, технически визуальные эффекты, для печати динамического пользовательского интерфейса), и там я использовал кисти. Еще одно редактирование! Я обещаю, что это будет идеальное решение! - person newb; 06.12.2012
comment
Я не мог понять, как изменить свойства по запросу. Я буду продолжать создавать новые CroppedBitmap. Кажется, это слишком сильно выталкивает процессор из-за того, что он делает, но пока я просто остановлюсь на нем. Спасибо (обоим) за вклад. - person jimasun; 07.12.2012