MythTV  master
osdchromakey.cpp
Go to the documentation of this file.
1 /* Based on xqcam.c by Paul Chinn <loomer@svpal.org> */
2 
3 // C headers
4 #include <cmath>
5 
6 // POSIX headers
7 #include <sys/ipc.h>
8 #include <sys/shm.h>
9 
10 // MythTV headers
11 #include "osd.h"
12 #include "osdchromakey.h"
13 
14 #include "mythlogging.h"
15 #include "videoout_xv.h"
16 #include "mythxdisplay.h"
17 
18 #ifdef MMX
19 extern "C" {
20 #include "ffmpeg-mmx.h"
21 }
22 #endif
23 
24 #define LOC QString("OSDChroma: ")
25 
27 {
28  TearDown();
29 }
30 
32 {
33  if (!videoOutput || area.isEmpty())
34  return false;
35 
36  uint size = 0;
37  MythXDisplay *disp = videoOutput->disp;
38  MythXLocker lock(disp);
39  Display *d = disp->GetDisplay();
40  int screen_num = disp->GetScreen();
41 
42  XImage *shm_img =
43  XShmCreateImage(d, DefaultVisual(d,screen_num),
44  disp->GetDepth(), ZPixmap, nullptr,
45  &shm_infos, area.width(),area.height());
46  if (shm_img)
47  size = shm_img->bytes_per_line * (shm_img->height+1) + 128;
48 
49  shm_infos.shmid = 0;
50  shm_infos.shmaddr = nullptr;
51  if (shm_img)
52  {
53  shm_infos.shmid = shmget(IPC_PRIVATE, size, IPC_CREAT|0777);
54  if (shm_infos.shmid >= 0)
55  {
56  shm_infos.shmaddr = (char*) shmat(shm_infos.shmid, nullptr, 0);
57 
58  shm_img->data = shm_infos.shmaddr;
59  shm_infos.readOnly = False;
60 
61  XShmAttach(d, &shm_infos);
62  disp->Sync(); // needed for FreeBSD?
63 
64  // Mark for delete immediately.
65  // It won't actually be removed until after we detach it.
66  shmctl(shm_infos.shmid, IPC_RMID, nullptr);
67  img = shm_img;
68  return true;
69  }
70  }
71  return false;
72 }
73 
75 {
76  if (!img || !videoOutput)
77  return;
78 
79  MythXDisplay *disp = videoOutput->disp;
80  disp->Lock();
81  XShmDetach(disp->GetDisplay(), &shm_infos);
82  XFree(img);
83  img = nullptr;
84  disp->Unlock();
85 
86  if (shm_infos.shmaddr)
87  shmdt(shm_infos.shmaddr);
88  if (shm_infos.shmid > 0)
89  shmctl(shm_infos.shmid, IPC_RMID, nullptr);
90 
91  memset(&shm_infos, 0, sizeof(XShmSegmentInfo));
92 }
93 
94 bool ChromaKeyOSD::Init(QSize new_size)
95 {
96  if (current_size == new_size)
97  return true;
98 
99  TearDown();
100 
101  bool success = CreateShmImage(new_size);
102  image = new QImage(new_size, QImage::Format_ARGB32_Premultiplied);
103  painter = new MythQImagePainter();
104 
105  if (success && image && painter)
106  {
107  current_size = new_size;
108  image->fill(0);
109  LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("Created ChromaOSD size %1x%2")
110  .arg(current_size.width())
111  .arg(current_size.height()));
112  return true;
113  }
114 
115  LOG(VB_PLAYBACK, LOG_ERR, LOC + QString("Failed to create ChromaOSD."));
116  return false;
117 }
118 
120 {
121  DestroyShmImage();
122 
123  if (image)
124  {
125  delete image;
126  image = nullptr;
127  }
128 
129  if (painter)
130  {
131  delete painter;
132  painter = nullptr;
133  }
134 }
135 
136 #define MASK 0xFE000000
137 #define MMX_MASK 0xFE000000FE000000LL
138 
139 void ChromaKeyOSD::BlendOrCopy(uint32_t colour, const QRect &rect)
140 {
141  int width = rect.width();
142  int height = rect.height();
143  if (!width || !height)
144  return;
145 
146  uint src_stride = image->bytesPerLine();
147  uint dst_stride = img->bytes_per_line;
148  unsigned char *src = image->bits() +
149  (rect.top() * src_stride) + (rect.left() << 2);
150  unsigned char *dst = (unsigned char*)shm_infos.shmaddr +
151  (rect.top() * dst_stride) + (rect.left() << 2);
152 
153  if (colour == 0x0)
154  {
155  for (int i = 0; i < height; i++)
156  {
157  memcpy(dst, src, width << 2);
158  src += src_stride;
159  dst += dst_stride;
160  }
161  return;
162  }
163 
164  src_stride = src_stride >> 2;
165  dst_stride = dst_stride >> 2;
166 
167 #ifdef MMX
168  bool odd_start = (rect.left() & 0x1) != 0;
169  bool odd_end = ((rect.left() + rect.width()) & 0x1) != 0;
170  static mmx_t mask = {MMX_MASK};
171  static mmx_t zero = {0x0000000000000000LL};
172  uint32_t *src_start = (uint32_t*)src;
173  uint32_t *dst_start = (uint32_t*)dst;
174  uint32_t *src_end = nullptr;
175  uint32_t *dst_end = nullptr;
176 
177  if (odd_end)
178  {
179  width--;
180  src_end = src_start + width;
181  dst_end = dst_start + width;
182  }
183 
184  if (odd_start)
185  {
186  src += 4; dst += 4;
187  width--;
188  }
189 
190  uint64_t *source = (uint64_t*)src;
191  uint64_t *dest = (uint64_t*)dst;
192 #else
193  uint32_t *source = (uint32_t*)src;
194  uint32_t *dest = (uint32_t*)dst;
195 #endif
196 
197  for (int i = 0; i < height; i++)
198  {
199 #ifdef MMX
200  if (odd_start)
201  {
202  dst_start[0] = (src_start[0] & MASK) ? src_start[0] : colour;
203  src_start += src_stride;
204  dst_start += dst_stride;
205  }
206 
207  if (odd_end)
208  {
209  dst_end[0] = (src_end[0] & MASK) ? src_end[0] : colour;
210  src_end += src_stride;
211  dst_end += dst_stride;
212  }
213 
214  punpckldq_m2r (colour, mm0);
215  punpckhdq_r2r (mm0, mm0);
216  for (int j = 0; j < (width >> 1); j++)
217  {
218  movq_m2r (source[j], mm1);
219  pand_m2r (mask, mm1);
220  pcmpeqd_m2r (zero, mm1);
221  movq_r2r (mm1, mm2);
222  pand_r2r (mm0, mm1);
223  pandn_m2r (source[j], mm2);
224  por_r2r (mm1, mm2);
225  movq_r2m (mm2, dest[j]);
226  }
227  source += src_stride >> 1;
228  dest += dst_stride >> 1;
229 #else
230  for (int j = 0; j < width; j++)
231  dest[j] = (source[j] & MASK) ? source[j] : colour;
232  source += src_stride;
233  dest += dst_stride;
234 #endif
235  }
236 
237 #ifdef MMX
238  emms();
239 #endif
240 }
241 
247 {
248  if (!osd || !videoOutput)
249  return false;
250 
251  QRect osd_rect = videoOutput->GetTotalOSDBounds();
252  if (!Init(osd_rect.size()))
253  return false;
254 
255  bool was_visible = visible;
256  QRect video_rect = videoOutput->window.GetDisplayVideoRect();
257  QRegion dirty = QRegion();
258  QRegion vis_area = osd->Draw(painter, image, current_size, dirty);
259  visible = !vis_area.isEmpty();
260 
261  if (dirty.isEmpty() && (video_rect == current_rect))
262  return (visible || was_visible);
263 
264  if (video_rect != current_rect)
265  dirty = osd_rect;
266 
267  current_rect = video_rect;
268  uint32_t letterbox = (uint32_t)videoOutput->XJ_letterbox_colour;
269  uint32_t colorkey = (uint32_t)videoOutput->xv_colorkey;
270 
271  int boboff = (int) round(
272  ((double)current_size.height()) / 456 - 0.00001);
273  boboff = (videoOutput->m_deinterlacing &&
274  videoOutput->m_deintfiltername == "bobdeint") ? boboff : 0;
275 
276  video_rect.adjust(0, boboff, 0, -boboff);
277  video_rect = video_rect.intersected(osd_rect);
278 
279  QRect top = QRect(0, 0, osd_rect.width(), video_rect.top());
280  QRect left = QRect(0, video_rect.top(), video_rect.left(), video_rect.height());
281  QRect right = QRect(video_rect.left() + video_rect.width(), video_rect.top(),
282  osd_rect.width() - video_rect.width() - video_rect.left(),
283  video_rect.height());
284  QRect bot = QRect(0, video_rect.top() + video_rect.height(), osd_rect.width(),
285  osd_rect.height() - video_rect.top() - video_rect.height());
286 
287 #if QT_VERSION < QT_VERSION_CHECK(5, 8, 0)
288  QVector<QRect> update = dirty.rects();
289  for (int i = 0; i < update.size(); i++)
290  {
291  const QRect& r = update[i];
292 #else
293  for (const QRect& r : dirty)
294  {
295 #endif
296  BlendOrCopy(letterbox, r.intersected(top));
297  BlendOrCopy(letterbox, r.intersected(left));
298  BlendOrCopy(colorkey, r.intersected(video_rect));
299  BlendOrCopy(letterbox, r.intersected(right));
300  BlendOrCopy(letterbox, r.intersected(bot));
301  }
302 
303  return (visible || was_visible);
304 }
Display * GetDisplay(void)
Definition: mythxdisplay.h:21
XImage * img
Definition: osdchromakey.h:36
QString m_deintfiltername
Definition: videooutbase.h:351
bool ProcessOSD(OSD *osd)
void DestroyShmImage(void)
void Unlock(void)
Definition: mythxdisplay.h:24
bool m_deinterlacing
Definition: videooutbase.h:350
#define round(x)
Definition: mythplayer.cpp:66
QRect GetTotalOSDBounds(void) const
Returns total OSD bounds.
void TearDown(void)
This file is intended to hold X11 specific utility functions.
Definition: mythxdisplay.h:16
void Lock(void)
Definition: mythxdisplay.h:23
unsigned int uint
Definition: compat.h:140
MythXDisplay * disp
Definition: videoout_xv.h:136
bool CreateShmImage(QSize area)
void Sync(bool flush=false)
unsigned char r
Definition: ParseText.cpp:329
XShmSegmentInfo shm_infos
Definition: osdchromakey.h:37
QImage * image
Definition: osdchromakey.h:38
int GetScreen(void) const
Definition: mythxdisplay.h:22
#define MMX_MASK
unsigned long XJ_letterbox_colour
Definition: videoout_xv.h:137
QRect current_rect
Definition: osdchromakey.h:34
int GetDepth(void) const
Definition: mythxdisplay.h:25
static const uint16_t * d
QRegion Draw(MythPainter *painter, QPaintDevice *device, QSize size, QRegion &changed, int alignx=0, int aligny=0)
Definition: osd.cpp:787
VideoOutputXv * videoOutput
Definition: osdchromakey.h:35
QRect GetDisplayVideoRect(void) const
bool Init(QSize new_size)
#define mmx_t
MythQImagePainter * painter
Definition: osdchromakey.h:39
#define LOG(_MASK_, _LEVEL_, _STRING_)
Definition: mythlogging.h:41
static int x0
Definition: mythsocket.cpp:59
#define MASK
#define emms()
Definition: mm_arch.h:15
Definition: osd.h:132
~ChromaKeyOSD(void)
VideoOutWindow window
Definition: videooutbase.h:320
#define LOC
QSize current_size
Definition: osdchromakey.h:33
static int x1
Definition: mythsocket.cpp:60
void BlendOrCopy(uint32_t colour, const QRect &rect)