00001
00002
00003
00004 #include <cmath>
00005
00006
00007 #include <sys/ipc.h>
00008 #include <sys/shm.h>
00009
00010
00011 #include "osd.h"
00012 #include "osdchromakey.h"
00013
00014 #include "mythlogging.h"
00015 #include "videoout_xv.h"
00016 #include "mythxdisplay.h"
00017
00018 #ifdef MMX
00019 extern "C" {
00020 #include "ffmpeg-mmx.h"
00021 }
00022 #endif
00023
00024 #define LOC QString("OSDChroma: ")
00025
00026 ChromaKeyOSD::~ChromaKeyOSD(void)
00027 {
00028 TearDown();
00029 }
00030
00031 bool ChromaKeyOSD::CreateShmImage(QSize area)
00032 {
00033 if (!videoOutput || area.isEmpty())
00034 return false;
00035
00036 uint size = 0;
00037 MythXDisplay *disp = videoOutput->disp;
00038 MythXLocker lock(disp);
00039 Display *d = disp->GetDisplay();
00040 int screen_num = disp->GetScreen();
00041
00042 XImage *shm_img =
00043 XShmCreateImage(d, DefaultVisual(d,screen_num),
00044 disp->GetDepth(), ZPixmap, 0,
00045 &shm_infos, area.width(),area.height());
00046 if (shm_img)
00047 size = shm_img->bytes_per_line * (shm_img->height+1) + 128;
00048
00049 shm_infos.shmid = 0;
00050 shm_infos.shmaddr = NULL;
00051 if (shm_img)
00052 {
00053 shm_infos.shmid = shmget(IPC_PRIVATE, size, IPC_CREAT|0777);
00054 if (shm_infos.shmid >= 0)
00055 {
00056 shm_infos.shmaddr = (char*) shmat(shm_infos.shmid, 0, 0);
00057
00058 shm_img->data = shm_infos.shmaddr;
00059 shm_infos.readOnly = False;
00060
00061 XShmAttach(d, &shm_infos);
00062 disp->Sync();
00063
00064
00065
00066 shmctl(shm_infos.shmid, IPC_RMID, 0);
00067 img = shm_img;
00068 return true;
00069 }
00070 }
00071 return false;
00072 }
00073
00074 void ChromaKeyOSD::DestroyShmImage(void)
00075 {
00076 if (!img || !videoOutput)
00077 return;
00078
00079 MythXDisplay *disp = videoOutput->disp;
00080 disp->Lock();
00081 XShmDetach(disp->GetDisplay(), &shm_infos);
00082 XFree(img);
00083 img = NULL;
00084 disp->Unlock();
00085
00086 if (shm_infos.shmaddr)
00087 shmdt(shm_infos.shmaddr);
00088 if (shm_infos.shmid > 0)
00089 shmctl(shm_infos.shmid, IPC_RMID, 0);
00090
00091 memset(&shm_infos, 0, sizeof(XShmSegmentInfo));
00092 }
00093
00094 bool ChromaKeyOSD::Init(QSize new_size)
00095 {
00096 if (current_size == new_size)
00097 return true;
00098
00099 TearDown();
00100
00101 bool success = CreateShmImage(new_size);
00102 image = new QImage(new_size, QImage::Format_ARGB32_Premultiplied);
00103 painter = new MythQImagePainter();
00104
00105 if (success && image && painter)
00106 {
00107 current_size = new_size;
00108 image->fill(0);
00109 LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("Created ChromaOSD size %1x%2")
00110 .arg(current_size.width())
00111 .arg(current_size.height()));
00112 return true;
00113 }
00114
00115 LOG(VB_PLAYBACK, LOG_ERR, LOC + QString("Failed to create ChromaOSD."));
00116 return false;
00117 }
00118
00119 void ChromaKeyOSD::TearDown(void)
00120 {
00121 DestroyShmImage();
00122
00123 if (image)
00124 {
00125 delete image;
00126 image = NULL;
00127 }
00128
00129 if (painter)
00130 {
00131 delete painter;
00132 painter = NULL;
00133 }
00134 }
00135
00136 #define MASK 0xFE000000
00137 #define MMX_MASK 0xFE000000FE000000LL
00138
00139 void ChromaKeyOSD::BlendOrCopy(uint32_t colour, const QRect &rect)
00140 {
00141 int width = rect.width();
00142 int height = rect.height();
00143 if (!width || !height)
00144 return;
00145
00146 uint src_stride = image->bytesPerLine();
00147 uint dst_stride = img->bytes_per_line;
00148 unsigned char *src = image->bits() +
00149 (rect.top() * src_stride) + (rect.left() << 2);
00150 unsigned char *dst = (unsigned char*)shm_infos.shmaddr +
00151 (rect.top() * dst_stride) + (rect.left() << 2);
00152
00153 if (colour == 0x0)
00154 {
00155 for (int i = 0; i < height; i++)
00156 {
00157 memcpy(dst, src, width << 2);
00158 src += src_stride;
00159 dst += dst_stride;
00160 }
00161 return;
00162 }
00163
00164 src_stride = src_stride >> 2;
00165 dst_stride = dst_stride >> 2;
00166
00167 #ifdef MMX
00168 bool odd_start = rect.left() & 0x1;
00169 bool odd_end = (rect.left() + rect.width()) & 0x1;
00170 static mmx_t mask = {MMX_MASK};
00171 static mmx_t zero = {0x0000000000000000LL};
00172 uint32_t *src_start = (uint32_t*)src;
00173 uint32_t *dst_start = (uint32_t*)dst;
00174 uint32_t *src_end = NULL;
00175 uint32_t *dst_end = NULL;
00176
00177 if (odd_end)
00178 {
00179 width--;
00180 src_end = src_start + width;
00181 dst_end = dst_start + width;
00182 }
00183
00184 if (odd_start)
00185 {
00186 src += 4; dst += 4;
00187 width--;
00188 }
00189
00190 uint64_t *source = (uint64_t*)src;
00191 uint64_t *dest = (uint64_t*)dst;
00192 #else
00193 uint32_t *source = (uint32_t*)src;
00194 uint32_t *dest = (uint32_t*)dst;
00195 #endif
00196
00197 for (int i = 0; i < height; i++)
00198 {
00199 #ifdef MMX
00200 if (odd_start)
00201 {
00202 dst_start[0] = (src_start[0] & MASK) ? src_start[0] : colour;
00203 src_start += src_stride;
00204 dst_start += dst_stride;
00205 }
00206
00207 if (odd_end)
00208 {
00209 dst_end[0] = (src_end[0] & MASK) ? src_end[0] : colour;
00210 src_end += src_stride;
00211 dst_end += dst_stride;
00212 }
00213
00214 punpckldq_m2r (colour, mm0);
00215 punpckhdq_r2r (mm0, mm0);
00216 for (int j = 0; j < (width >> 1); j++)
00217 {
00218 movq_m2r (source[j], mm1);
00219 pand_m2r (mask, mm1);
00220 pcmpeqd_m2r (zero, mm1);
00221 movq_r2r (mm1, mm2);
00222 pand_r2r (mm0, mm1);
00223 pandn_m2r (source[j], mm2);
00224 por_r2r (mm1, mm2);
00225 movq_r2m (mm2, dest[j]);
00226 }
00227 source += src_stride >> 1;
00228 dest += dst_stride >> 1;
00229 #else
00230 for (int j = 0; j < width; j++)
00231 dest[j] = (source[j] & MASK) ? source[j] : colour;
00232 source += src_stride;
00233 dest += dst_stride;
00234 #endif
00235 }
00236
00237 #ifdef MMX
00238 emms();
00239 #endif
00240 }
00241
00246 bool ChromaKeyOSD::ProcessOSD(OSD *osd)
00247 {
00248 if (!osd || !videoOutput)
00249 return false;
00250
00251 QRect osd_rect = videoOutput->GetTotalOSDBounds();
00252 if (!Init(osd_rect.size()))
00253 return false;
00254
00255 bool was_visible = visible;
00256 QRect video_rect = videoOutput->window.GetDisplayVideoRect();
00257 QRegion dirty = QRegion();
00258 QRegion vis_area = osd->Draw(painter, image, current_size, dirty);
00259 visible = !vis_area.isEmpty();
00260
00261 if (dirty.isEmpty() && (video_rect == current_rect))
00262 return (visible || was_visible);
00263
00264 if (video_rect != current_rect)
00265 dirty = osd_rect;
00266
00267 current_rect = video_rect;
00268 uint32_t letterbox = (uint32_t)videoOutput->XJ_letterbox_colour;
00269 uint32_t colorkey = (uint32_t)videoOutput->xv_colorkey;
00270
00271 int boboff = (int) round(
00272 ((double)current_size.height()) / 456 - 0.00001);
00273 boboff = (videoOutput->m_deinterlacing &&
00274 videoOutput->m_deintfiltername == "bobdeint") ? boboff : 0;
00275
00276 video_rect.adjust(0, boboff, 0, -boboff);
00277 video_rect = video_rect.intersected(osd_rect);
00278
00279 QRect top = QRect(0, 0, osd_rect.width(), video_rect.top());
00280 QRect left = QRect(0, video_rect.top(), video_rect.left(), video_rect.height());
00281 QRect right = QRect(video_rect.left() + video_rect.width(), video_rect.top(),
00282 osd_rect.width() - video_rect.width() - video_rect.left(),
00283 video_rect.height());
00284 QRect bot = QRect(0, video_rect.top() + video_rect.height(), osd_rect.width(),
00285 osd_rect.height() - video_rect.top() - video_rect.height());
00286
00287 QVector<QRect> update = dirty.rects();
00288 for (int i = 0; i < update.size(); i++)
00289 {
00290 BlendOrCopy(letterbox, update[i].intersected(top));
00291 BlendOrCopy(letterbox, update[i].intersected(left));
00292 BlendOrCopy(colorkey, update[i].intersected(video_rect));
00293 BlendOrCopy(letterbox, update[i].intersected(right));
00294 BlendOrCopy(letterbox, update[i].intersected(bot));
00295 }
00296
00297 return (visible || was_visible);
00298 }
00299