1
/*  This file is part of the KDE project.
2
3
Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
4
5
This library is free software: you can redistribute it and/or modify
6
it under the terms of the GNU Lesser General Public License as published by
7
the Free Software Foundation, either version 2.1 or 3 of the License.
8
9
This library is distributed in the hope that it will be useful,
10
but WITHOUT ANY WARRANTY; without even the implied warranty of
11
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
GNU Lesser General Public License for more details.
13
14
You should have received a copy of the GNU Lesser General Public License
15
along with this library.  If not, see <http://www.gnu.org/licenses/>.
16
*/
17
18
19
#include "videorenderer_vmr9.h"
20
21
#ifndef QT_NO_PHONON_VIDEO
22
23
#include <QtGui/QWidget>
24
#include <QtGui/QPainter>
25
26
#include <d3d9.h>
27
#include <vmr9.h>
28
29
QT_BEGIN_NAMESPACE
30
31
32
namespace Phonon
33
{
34
    namespace DS9
35
    {
36
        VideoRendererVMR9::~VideoRendererVMR9()
37
        {
38
        }
39
40
        bool VideoRendererVMR9::isNative() const
41
        {
42
            return true;
43
        }
44
45
46
        VideoRendererVMR9::VideoRendererVMR9(QWidget *target) : m_target(target)
47
        {
48
            m_filter = Filter(CLSID_VideoMixingRenderer9, IID_IBaseFilter);
49
            if (!m_filter) {
50
                return;
51
            }
52
53
            ComPointer<IVMRFilterConfig9> config(m_filter, IID_IVMRFilterConfig9);
54
            Q_ASSERT(config);
55
            HRESULT hr = config->SetRenderingMode(VMR9Mode_Windowless);
56
            Q_ASSERT(SUCCEEDED(hr));
57
            hr = config->SetNumberOfStreams(1); //for now we limit it to 1 input stream
58
            Q_ASSERT(SUCCEEDED(hr));
59
            ComPointer<IVMRWindowlessControl9> windowlessControl(m_filter, IID_IVMRWindowlessControl9);
60
            windowlessControl->SetVideoClippingWindow(reinterpret_cast<HWND>(target->winId()));
61
            windowlessControl->SetAspectRatioMode(VMR9ARMode_None); //we're in control of the size
62
        }
63
64
        QImage VideoRendererVMR9::snapshot() const
65
        {
66
            ComPointer<IVMRWindowlessControl9> windowlessControl(m_filter, IID_IVMRWindowlessControl9);
67
            if (windowlessControl) {
68
                BYTE *buffer = 0;
69
                HRESULT hr = windowlessControl->GetCurrentImage(&buffer);
70
                if (SUCCEEDED(hr)) {
71
72
                    const BITMAPINFOHEADER  *bmi = reinterpret_cast<BITMAPINFOHEADER*>(buffer);
73
                    const int w = qAbs(bmi->biWidth),
74
                        h = qAbs(bmi->biHeight);
75
76
                    // Create image and copy data into image.
77
                    QImage ret(w, h, QImage::Format_RGB32);
78
79
                    if (!ret.isNull()) {
80
                        uchar *data = buffer + bmi->biSize;
81
                        const int bytes_per_line = w * sizeof(QRgb);
82
                        for (int y = h - 1; y >= 0; --y) {
83
                            qMemCopy(ret.scanLine(y), //destination
84
                                data,     //source
85
                                bytes_per_line);
86
                            data += bytes_per_line;
87
                        }
88
                    }
89
                    ::CoTaskMemFree(buffer);
90
                    return ret;
91
                }
92
            }
93
            return QImage();
94
        }
95
96
        QSize VideoRendererVMR9::videoSize() const
97
        {
98
            LONG w = 0,
99
                h = 0;
100
            ComPointer<IVMRWindowlessControl9> windowlessControl(m_filter, IID_IVMRWindowlessControl9);
101
            if (windowlessControl) {
102
                windowlessControl->GetNativeVideoSize( &w, &h, 0, 0);
103
            }
104
            return QSize(w, h);
105
        }
106
       
107
        void VideoRendererVMR9::repaintCurrentFrame(QWidget *target, const QRect &rect)
108
        {
109
            HDC hDC = target->getDC();
110
            // repaint the video
111
            ComPointer<IVMRWindowlessControl9> windowlessControl(m_filter, IID_IVMRWindowlessControl9);
112
113
            HRESULT hr = windowlessControl ? windowlessControl->RepaintVideo(target->winId(), hDC) : E_POINTER;
114
            if (FAILED(hr) || m_dstY > 0 || m_dstX > 0) {
115
                const QColor c = target->palette().color(target->backgroundRole());
116
                COLORREF color = RGB(c.red(), c.green(), c.blue());
117
                HPEN hPen = ::CreatePen(PS_SOLID, 1, color);
118
                HBRUSH hBrush = ::CreateSolidBrush(color);
119
                ::SelectObject(hDC, hPen);
120
                ::SelectObject(hDC, hBrush);
121
                // repaint the video
122
                if (FAILED(hr)) {
123
                    //black background : we use the Win32 API to avoid the ghost effect of the backing store
124
                    ::Rectangle(hDC, 0, 0, target->width(), target->height());
125
                } else {
126
                    if (m_dstY > 0) {
127
                        ::Rectangle(hDC, 0, 0, target->width(), m_dstY); //top
128
                        ::Rectangle(hDC, 0, target->height() - m_dstY, target->width(), target->height()); //bottom
129
                    }
130
                    if (m_dstX > 0) {
131
                        ::Rectangle(hDC, 0, m_dstY, m_dstX, m_dstHeight + m_dstY); //left
132
                        ::Rectangle(hDC, m_dstWidth + m_dstX, m_dstY, target->width(), m_dstHeight + m_dstY); //right
133
                    }
134
                }
135
                ::DeleteObject(hPen);
136
                ::DeleteObject(hBrush);
137
            }
138
            target->releaseDC(hDC);
139
140
        }
141
142
        void VideoRendererVMR9::notifyResize(const QSize &size, Phonon::VideoWidget::AspectRatio aspectRatio,
143
            Phonon::VideoWidget::ScaleMode scaleMode)
144
        {
145
            if (!isActive()) {
146
                RECT dummyRect = { 0, 0, 0, 0};
147
                ComPointer<IVMRWindowlessControl9> windowlessControl(m_filter, IID_IVMRWindowlessControl9);
148
                windowlessControl->SetVideoPosition(&dummyRect, &dummyRect);
149
                return;
150
            }
151
152
153
            const QSize vsize = videoSize();
154
            internalNotifyResize(size, vsize, aspectRatio, scaleMode);
155
156
            RECT dstRectWin = { m_dstX, m_dstY, m_dstWidth + m_dstX, m_dstHeight + m_dstY};
157
            RECT srcRectWin = { 0, 0, vsize.width(), vsize.height()};
158
159
            ComPointer<IVMRWindowlessControl9> windowlessControl(m_filter, IID_IVMRWindowlessControl9);
160
            if (windowlessControl) {
161
                windowlessControl->SetVideoPosition(&srcRectWin, &dstRectWin);
162
            }
163
        }
164
165
        void VideoRendererVMR9::applyMixerSettings(qreal brightness, qreal contrast, qreal hue, qreal saturation)
166
        {
167
            InputPin sink = BackendNode::pins(m_filter, PINDIR_INPUT).first();
168
            OutputPin source;
169
            if (FAILED(sink->ConnectedTo(source.pparam()))) {
170
                return; //it must be connected to work
171
            }
172
173
            //get the mixer (used for brightness/contrast/saturation/hue)
174
            ComPointer<IVMRMixerControl9> mixer(m_filter, IID_IVMRMixerControl9);
175
            Q_ASSERT(mixer);
176
177
            VMR9ProcAmpControl ctrl;
178
            ctrl.dwSize = sizeof(ctrl);
179
            ctrl.dwFlags = ProcAmpControl9_Contrast | ProcAmpControl9_Brightness | ProcAmpControl9_Saturation | ProcAmpControl9_Hue;
180
            VMR9ProcAmpControlRange range;
181
            range.dwSize = sizeof(range);
182
183
            range.dwProperty = ProcAmpControl9_Contrast;
184
            HRESULT hr = mixer->GetProcAmpControlRange(0, &range);
185
            if (FAILED(hr)) {
186
                return;
187
            }
188
            ctrl.Contrast = ((contrast < 0 ? range.MinValue : range.MaxValue) - range.DefaultValue) * qAbs(contrast) + range.DefaultValue;
189
190
            //brightness
191
            range.dwProperty = ProcAmpControl9_Brightness;
192
            hr = mixer->GetProcAmpControlRange(0, &range);
193
            if (FAILED(hr)) {
194
                return;
195
            }
196
            ctrl.Brightness = ((brightness < 0 ? range.MinValue : range.MaxValue) - range.DefaultValue) * qAbs(brightness) + range.DefaultValue;
197
198
            //saturation
199
            range.dwProperty = ProcAmpControl9_Saturation;
200
            hr = mixer->GetProcAmpControlRange(0, &range);
201
            if (FAILED(hr)) {
202
                return;
203
            }
204
            ctrl.Saturation = ((saturation < 0 ? range.MinValue : range.MaxValue) - range.DefaultValue) * qAbs(saturation) + range.DefaultValue;
205
206
            //hue
207
            range.dwProperty = ProcAmpControl9_Hue;
208
            hr = mixer->GetProcAmpControlRange(0, &range);
209
            if (FAILED(hr)) {
210
                return;
211
            }
212
            ctrl.Hue = ((hue < 0 ? range.MinValue : range.MaxValue) - range.DefaultValue) * qAbs(hue) + range.DefaultValue;
213
214
            //finally set the settings
215
            mixer->SetProcAmpControl(0, &ctrl);
216
        }
217
    }
218
}
219
220
QT_END_NAMESPACE
221
222
#endif //QT_NO_PHONON_VIDEO