1 /* ***** BEGIN LICENSE BLOCK *****
7 * Copyright (c) 2008 BBC Research
9 * Permission is hereby granted, free of charge, to any person obtaining a copy
10 * of this software and associated documentation files (the "Software"), to deal
11 * in the Software without restriction, including without limitation the rights
12 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13 * copies of the Software, and to permit persons to whom the Software is
14 * furnished to do so, subject to the following conditions:
16 * The above copyright notice and this permission notice shall be included in
17 * all copies or substantial portions of the Software.
19 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
27 * ***** END LICENSE BLOCK ***** */
31 #include "chromaResample.h"
32 #include "colourSpace.h"
38 View::View(QWidget
*parent
) :
41 setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOn
);
42 setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn
);
43 setDragMode(QGraphicsView::ScrollHandDrag
);
49 //YUV image display defaults
50 yuvDisplayMode
= YUVdisplayColour
;
51 yuvImageNativeBitDepth
= 10;
52 yuvImageBitDepth
= 10;
53 yuvImageRGBScaling
= true;
54 yuvImageColourMatrix
= ColourSpace::HDTVtoRGB
;
56 inChroma
= RawFrame::Cr422
;
57 inFrame
= new RawFrame(0, 0, inChroma
);
58 frame444
= new RawFrame(0, 0, RawFrame::Cr444
);
59 frameRGB
= new RawFrame(0, 0, RawFrame::CrRGB
);
61 scene
= new QGraphicsScene
;
64 QBrush brush
= QBrush(QColor(32,32,32), Qt::SolidPattern
);
65 setBackgroundBrush(brush
);
68 void View::setupMatrix()
71 matrix
.scale(zoom
, zoom
);
72 matrix
.rotate(rotation
);
77 void View::showFrame(const RawFrame
*f
)
79 inFrame
= (RawFrame
*)f
;
80 processImage(processResample
);
83 //erase the input and display data to ensure that the displayed image
84 //is removed when opening the next frame
85 void View::invalidateFrame()
87 if(inFrame
->chroma
== RawFrame::CrRGB
) {
92 //this will result in a illegal green frame - ie something is wrong
93 inFrame
->luma
.assign(0);
94 inFrame
->cb
.assign(0);
95 inFrame
->cr
.assign(0);
99 void View::setZoom(float value
)
104 emit(zoomChanged(zoom
));
108 float View::getZoom(void)
110 //this assume sthat the transformation matrix m11 and m22 are the same, so that there is
111 //the same zoom ratio in x/y
112 return matrix().m11();
115 int View::getRotation(void)
120 //change the mode used to display the YCbCr image (colour / Yonly / planar)
121 void View::setYUVdisplayMode(YUVdisplayMode mode
){
122 if(mode
!= yuvDisplayMode
) {
123 yuvDisplayMode
= mode
;
124 processImage(processColour
);
128 //set the scaling used when converting from YCbCr to RGB, and reprocess the input
129 void View::setYUVRGBScaling(bool scaled
) {
130 if(scaled
!= yuvImageRGBScaling
) {
131 yuvImageRGBScaling
= scaled
;
132 processImage(processColour
);
136 //the GUI can request a particular bit depth to use for display
137 void View::setYUVbitDepth(int bits
)
139 if (bits
!= yuvImageBitDepth
) {
140 yuvImageBitDepth
= bits
;
141 processImage(processColour
);
145 //the original file can have a native bit depth which may be 0 if unknown
146 void View::setYUVnativeBitDepth(int bits
)
148 yuvImageNativeBitDepth
= bits
;
151 //set the colour matrix type and reprocess the image
152 void View::setYUVColourMatrix(ColourSpace::MatrixType type
)
154 if(type
!= yuvImageColourMatrix
) {
155 yuvImageColourMatrix
= type
;
156 processImage(processColour
);
160 void View::processImage(const ProcessType type
)
162 QImage
displayImage(inFrame
->luma
.width(), inFrame
->luma
.height(), QImage::Format_RGB32
);
164 QImage
*planarY
= NULL
;
165 QImage
*planarCb
= NULL
;
166 QImage
*planarCr
= NULL
;
168 //these are zero sized as the process_ext methods are used on external data
169 ChromaResample
chromaResample(0, 0, RawFrame::Cr444
);
170 ColourSpace
colourSpace(0, 0, RawFrame::CrRGB
);
172 //default to the native bit depth of the image
173 int depth
= yuvImageNativeBitDepth
;
175 //if the bit depth cannot be determined, take the one from the GUI
176 if(depth
== 0) depth
= yuvImageBitDepth
;
180 case processResample
:
181 //check that the 444 frame is the right size
182 if(frame444
->luma
.width() != inFrame
->luma
.width() ||
183 frame444
->luma
.height() != inFrame
->luma
.height()) {
185 frame444
= new RawFrame(inFrame
->luma
.width(), inFrame
->luma
.height(), RawFrame::Cr444
);
188 if(inFrame
->chroma
!= RawFrame::CrRGB
) {
189 chromaResample
.process_ext(*inFrame
, *frame444
);
192 //copying - inefficient but useful for later
193 frame444
->r
= inFrame
->r
;
194 frame444
->g
= inFrame
->g
;
195 frame444
->b
= inFrame
->b
;
200 //check that the RGB frame is the right size
201 if(frameRGB
->luma
.width() != frame444
->luma
.width() ||
202 frameRGB
->luma
.height() != frame444
->luma
.height()) {
204 frameRGB
= new RawFrame(inFrame
->luma
.width(), inFrame
->luma
.height(), RawFrame::CrRGB
);
207 if(inFrame
->chroma
!= RawFrame::CrRGB
) {
209 if(inFrame
->chroma
== RawFrame::YOnly
|| yuvDisplayMode
== YUVdisplayYOnly
|| yuvDisplayMode
== YUVdisplayPlanar
) YOnly
= true;
210 colourSpace
.process_ext(*frame444
, *frameRGB
, yuvImageColourMatrix
, yuvImageRGBScaling
, YOnly
, depth
);
213 //more inefficient copying
214 frameRGB
->r
= frame444
->r
;
215 frameRGB
->g
= frame444
->g
;
216 frameRGB
->b
= frame444
->b
;
222 //copy the data to a QImage
223 for(int y
=0; y
<frameRGB
->r
.height(); y
++) {
224 for(int x
=0; x
<frameRGB
->r
.width(); x
++) {
226 int R
= frameRGB
->r
[y
][x
] >> (depth
- 8);
227 int G
= frameRGB
->g
[y
][x
] >> (depth
- 8);
228 int B
= frameRGB
->b
[y
][x
] >> (depth
- 8);
230 int pixel
= 0xff << 24 | R
<< 16 | G
<< 8 | B
;
232 displayImage
.setPixel(x
, y
, pixel
);
236 //generate seperate QImages for the chrominance if we are doing planar display
237 if(yuvDisplayMode
== YUVdisplayPlanar
&& inFrame
->chroma
!= RawFrame::YOnly
) {
238 planarY
= new QImage(inFrame
->luma
.width(), inFrame
->luma
.height(), QImage::Format_RGB32
);
239 planarCb
= new QImage(inFrame
->cb
.width(), inFrame
->cr
.height(), QImage::Format_RGB32
);
240 planarCr
= new QImage(inFrame
->cr
.width(), inFrame
->cr
.height(), QImage::Format_RGB32
);
243 for(int y
=0; y
<inFrame
->luma
.height(); y
++) {
244 for(int x
=0; x
<inFrame
->luma
.width(); x
++) {
246 int C
= inFrame
->luma
[y
][x
] >> (depth
- 8);
247 int pixel
= C
<< 16 | C
<< 8 | C
;
248 planarY
->setPixel(x
, y
, pixel
);
253 for(int y
=0; y
<inFrame
->cb
.height(); y
++) {
254 for(int x
=0; x
<inFrame
->cb
.width(); x
++) {
256 int C
= inFrame
->cb
[y
][x
] >> (depth
- 8);
257 int pixel
= C
<< 16 | C
<< 8 | C
;
258 planarCb
->setPixel(x
, y
, pixel
);
263 for(int y
=0; y
<inFrame
->cr
.height(); y
++) {
264 for(int x
=0; x
<inFrame
->cr
.width(); x
++) {
266 int C
= inFrame
->cr
[y
][x
] >> (depth
- 8);
267 int pixel
= C
<< 16 | C
<< 8 | C
;
268 planarCr
->setPixel(x
, y
, pixel
);
278 //Put the image on the graphics scene, and set the graphics view
281 scene
= new QGraphicsScene
;
283 //remove all items from the scene
284 foreach (QGraphicsItem
*item
, scene
->items()) {
285 scene
->removeItem(item
);
289 //add the planar format chroma if required
290 if(yuvDisplayMode
== YUVdisplayPlanar
&& inFrame
->chroma
!= RawFrame::YOnly
) {
293 QGraphicsPixmapItem
* yItem
= scene
->addPixmap(QPixmap::fromImage(*planarY
));
294 yItem
->setCursor(Qt::ArrowCursor
);
297 QGraphicsPixmapItem
*cbItem
= scene
->addPixmap(QPixmap::fromImage(*planarCb
));
298 cbItem
->setPos(QPoint(0, inFrame
->luma
.height()));
299 cbItem
->setCursor(Qt::ArrowCursor
);
301 //Cr to the right or below
302 if(inFrame
->chroma
== RawFrame::Cr444
|| inFrame
->chroma
== RawFrame::CrRGB
) {
303 //the resulting image is three times the original height
304 QGraphicsPixmapItem
*crItem
= scene
->addPixmap(QPixmap::fromImage(*planarCr
));
305 crItem
->setPos(QPoint(0, inFrame
->luma
.height()*2));
306 crItem
->setCursor(Qt::ArrowCursor
);
307 scene
->setSceneRect(0, 0, inFrame
->luma
.width(), inFrame
->luma
.height() * 3);
310 //we can fit the two pieces of chroma next to each other
311 QGraphicsPixmapItem
*crItem
= scene
->addPixmap(QPixmap::fromImage(*planarCr
));
312 crItem
->setPos(QPoint(inFrame
->luma
.width()/2, inFrame
->luma
.height()));
313 crItem
->setCursor(Qt::ArrowCursor
);
314 scene
->setSceneRect(0, 0, inFrame
->luma
.width(), inFrame
->luma
.height() + inFrame
->cb
.height());
319 //the scene only encompasses one colour or YOnly pixmap
320 QGraphicsPixmapItem
* pixmapItem
= scene
->addPixmap(QPixmap::fromImage(displayImage
));
321 pixmapItem
->setCursor(Qt::ArrowCursor
);
322 scene
->setSceneRect(0, 0, inFrame
->luma
.width(), inFrame
->luma
.height());
330 //these will only have been used if displaying planar format
336 //make the mouse wheel zoom in and out over the pointer position
337 void View::wheelEvent(QWheelEvent
* wheelEvent
)
341 if(wheelEvent
->delta()> 0)
346 //prevent extreme zooming out
350 //resize about the mouse position
351 setTransformationAnchor(AnchorUnderMouse
);
353 setTransformationAnchor(AnchorViewCenter
);
356 //double click shows pixel info
357 void View::mouseDoubleClickEvent(QMouseEvent
*e
)
362 //this is very neat. It returns the click position on the displayed pixmap
363 //accounting for the zoom/rotation of the scene
364 QPoint
viewPoint(mousex
, mousey
);
365 QPointF scenePoint
= mapToScene(viewPoint
);
367 int pixelx
= (int)scenePoint
.x();
368 int pixely
= (int)scenePoint
.y();
370 //it is possible to zoom out then double click outside the scene.
371 //do not try to use these coordinates to index our arrays of pixels
372 if(pixelx
< 0 || pixelx
> sceneRect().width()) return;
373 if(pixely
< 0 || pixely
> sceneRect().height()) return;
377 if(yuvDisplayMode
!= YUVdisplayPlanar
) {
379 if(inFrame
->chroma
!= RawFrame::CrRGB
) {
380 //YUV and RGB colour display
381 pixelInfo
= QString("Pixel at (%1,%2) RGB=[%3,%4,%5] YCbCr=[%6,%7,%8]")
382 .arg(pixelx
) .arg(pixely
)
383 .arg(frameRGB
->r
[pixely
][pixelx
]) .arg(frameRGB
->g
[pixely
][pixelx
]) .arg(frameRGB
->b
[pixely
][pixelx
])
384 .arg(frame444
->luma
[pixely
][pixelx
]) .arg(frame444
->cb
[pixely
][pixelx
]) .arg(frame444
->cr
[pixely
][pixelx
]);
388 pixelInfo
= QString("Pixel at (%1,%2) RGB=[%3,%4,%5]")
389 .arg(pixelx
) .arg(pixely
)
390 .arg(frameRGB
->r
[pixely
][pixelx
]) .arg(frameRGB
->g
[pixely
][pixelx
]) .arg(frameRGB
->b
[pixely
][pixelx
]);
395 if(inFrame
->chroma
== RawFrame::Cr444
|| inFrame
->chroma
== RawFrame::CrRGB
) {
397 //chroma stacked one above the other
399 if(pixely
< inFrame
->luma
.height()) {
401 pixelInfo
= QString("Pixel at (%1,%2) RGB=[%3,%4,%5] Y=[%6]")
402 .arg(pixelx
) .arg(pixely
)
403 .arg(frameRGB
->r
[pixely
][pixelx
]) .arg(frameRGB
->g
[pixely
][pixelx
]) .arg(frameRGB
->b
[pixely
][pixelx
])
404 .arg(inFrame
->luma
[pixely
][pixelx
]);
406 else if(pixely
< inFrame
->luma
.height() * 2) {
408 int cby
= pixely
- inFrame
->luma
.height();
409 pixelInfo
= QString("Cb Pixel at (%1,%2) Cb=[%3]")
410 .arg(pixelx
) .arg(cby
)
411 .arg(inFrame
->cb
[cby
][pixelx
]);
415 int cry
= pixely
- (inFrame
->luma
.height() * 2);
416 pixelInfo
= QString("Cr Pixel at (%1,%2) Cr=[%3]")
417 .arg(pixelx
) .arg(cry
)
418 .arg(inFrame
->cr
[cry
][pixelx
]);
422 //chroma side by side
423 if(pixely
< inFrame
->luma
.height()) {
425 pixelInfo
= QString("Pixel at (%1,%2) RGB=[%3,%4,%5] Y=[%6]")
426 .arg(pixelx
) .arg(pixely
)
427 .arg(frameRGB
->r
[pixely
][pixelx
]) .arg(frameRGB
->g
[pixely
][pixelx
]) .arg(frameRGB
->b
[pixely
][pixelx
])
428 .arg(inFrame
->luma
[pixely
][pixelx
]);
430 else if (pixelx
< (inFrame
->luma
.width() / 2)) {
432 if(pixelx
> inFrame
->cb
.width()) return; //cannot look in 4:1:1 where there is no data
433 int cby
= pixely
- inFrame
->luma
.height();
434 pixelInfo
= QString("Cb Pixel at (%1,%2) Cb=[%3]")
435 .arg(pixelx
) .arg(cby
)
436 .arg(inFrame
->cb
[cby
][pixelx
]);
440 int crx
= pixelx
- (inFrame
->luma
.width() / 2);
441 if(crx
> inFrame
->cr
.width()) return; //cannot look in 4:1:1 where there is no data
442 int cry
= pixely
- inFrame
->luma
.height();
443 pixelInfo
= QString("Cr Pixel at (%1,%2) Cr=[%3]")
445 .arg(inFrame
->cr
[cry
][crx
]);
450 emit(pixelInfoMessage(pixelInfo
, 0));