MythTV  0.26-pre
osdchromakey.cpp
Go to the documentation of this file.
00001 /* Based on xqcam.c by Paul Chinn <loomer@svpal.org> */
00002 
00003 // C headers
00004 #include <cmath>
00005 
00006 // POSIX headers
00007 #include <sys/ipc.h>
00008 #include <sys/shm.h>
00009 
00010 // MythTV headers
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(); // needed for FreeBSD?
00063 
00064             // Mark for delete immediately.
00065             // It won't actually be removed until after we detach it.
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 
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends