Hi, the first patch optimizes cNitFilter::Process() by looping only over the relevant channels. It introduces a further hash in cChannels and implements an iterator for hiding this implementation detail. The channel_display patch avoids to call DrawText() when the "date" text has not changed in the channel display. As DrawText() calls DrawRectangle() when a non transparent background color was specified, drawing the same text will always yield a dirty area although there is no difference when looking at the bitmap before and after the call to DrawText(). And finally, the formerly released osd_draw patch has been updated a little bit. DrawBitmap() and DrawRectangle() use now memcpy() when possible. Bye. -- Dipl.-Inform. (FH) Reinhard Nissl mailto:rnissl@xxxxxx
--- ../vdr-1.5.12-orig/channels.h 2007-09-02 12:23:11.000000000 +0200 +++ channels.h 2007-12-28 22:56:02.000000000 +0100 @@ -219,13 +219,49 @@ public: void SetRefChannel(cChannel *RefChannel); }; +class cIteratorImpl { +private: + int refCount; + cIteratorImpl(const cIteratorImpl &); + const cIteratorImpl &operator =(const cIteratorImpl &); +public: + cIteratorImpl(void) { refCount = 0; } + virtual ~cIteratorImpl() {} + virtual int AddRef(void) { return ++refCount; } + virtual int DelRef(void) { int RefCount = --refCount; if (RefCount <= 0) delete this; return RefCount; } + virtual void *First(void) = 0; + virtual void *Last(void) = 0; + virtual void *Prev(void) = 0; + virtual void *Next(void) = 0; + virtual void *Current(void) const = 0; + }; + +template <class T> class cIterator +{ +private: + cIteratorImpl *impl; +public: + cIterator(cIteratorImpl *Impl) { impl = Impl; impl->AddRef(); } + cIterator(const cIterator &rhs) { impl = rhs.impl; impl->AddRef(); } + ~cIterator() { impl->DelRef(); } + const cIterator &operator =(const cIterator &rhs) { rhs.impl->AddRef(); impl->DelRef(); impl = rhs.impl; return *this; } + T *First(void) const { return (T *)impl->First(); } + T *Last(void) const { return (T *)impl->Last(); } + T *Prev(void) const { return (T *)impl->Prev(); } + T *Next(void) const { return (T *)impl->Next(); } + T *Current(void) const { return (T *)impl->Current(); } + }; + class cChannels : public cRwLock, public cConfig<cChannel> { private: int maxNumber; int modified; int beingEdited; cHash<cChannel> channelsHashSid; + cHash<cChannel> channelsHashNidTid; void DeleteDuplicateChannels(void); + void ClearChannelHashes(void); + static unsigned int HashKeyNidTid(unsigned short Nid, unsigned short Tid); public: cChannels(void); bool Load(const char *FileName, bool AllowComments = false, bool MustExist = false); @@ -240,6 +276,7 @@ public: cChannel *GetByServiceID(int Source, int Transponder, unsigned short ServiceID); cChannel *GetByChannelID(tChannelID ChannelID, bool TryWithoutRid = false, bool TryWithoutPolarization = false); cChannel *GetByTransponderID(tChannelID ChannelID); + cIterator<cChannel> GetChannelsBySourceNidTid(int Source, unsigned short Nid, unsigned short Tid); int BeingEdited(void) { return beingEdited; } void IncBeingEdited(void) { beingEdited++; } void DecBeingEdited(void) { beingEdited--; } --- ../vdr-1.5.12-orig/channels.c 2007-10-12 16:40:53.000000000 +0200 +++ channels.c 2007-12-28 22:56:02.000000000 +0100 @@ -905,14 +905,72 @@ bool cChannels::Load(const char *FileNam return false; } +void cChannels::ClearChannelHashes(void) +{ + channelsHashSid.Clear(); + channelsHashNidTid.Clear(); +} + void cChannels::HashChannel(cChannel *Channel) { channelsHashSid.Add(Channel, Channel->Sid()); + channelsHashNidTid.Add(Channel, HashKeyNidTid(Channel->Nid(), Channel->Tid())); } void cChannels::UnhashChannel(cChannel *Channel) { channelsHashSid.Del(Channel, Channel->Sid()); + channelsHashNidTid.Del(Channel, HashKeyNidTid(Channel->Nid(), Channel->Tid())); +} + +unsigned int cChannels::HashKeyNidTid(unsigned short Nid, unsigned short Tid) +{ + return Nid << 16 | Tid; +} + +cIterator<cChannel> cChannels::GetChannelsBySourceNidTid(int Source, unsigned short Nid, unsigned short Tid) +{ + class cIteratorImplSourceNidTid : public cIteratorImpl { + private: + cList<cHashObject> *hashList; + cHashObject *current; + int source; + unsigned short nid; + unsigned short tid; + cChannel *FindMatchingChannel(bool reverse, bool reset = false) { + if (!hashList || (!current && !reset)) + return NULL; + while (true) { + if (reset) { + reset = false; + current = reverse ? hashList->Last() : hashList->First(); + } + else + current = reverse ? hashList->Prev(current) : hashList->Next(current); + if (!current) + break; + cChannel *Channel = (cChannel *)current->Object(); + if (Channel->Source() == source && Channel->Nid() == nid && Channel->Tid() == tid) + return Channel; + } + return NULL; + } + public: + cIteratorImplSourceNidTid(cList<cHashObject> *HashList, int Source, unsigned short Nid, unsigned short Tid) { + hashList = HashList; + source = Source; + nid = Nid; + tid = Tid; + current = NULL; + } + virtual void *First(void) { return FindMatchingChannel(false, true); } + virtual void *Last(void) { return FindMatchingChannel(true, true); } + virtual void *Prev(void) { return FindMatchingChannel(false); } + virtual void *Next(void) { return FindMatchingChannel(true); } + virtual void *Current(void) const { return current ? (cChannel *)current->Object() : NULL; } + }; + + return cIterator<cChannel>(new cIteratorImplSourceNidTid(channelsHashNidTid.GetList(HashKeyNidTid(Nid, Tid)), Source, Nid, Tid)); } int cChannels::GetNextGroup(int Idx) @@ -949,7 +1007,7 @@ int cChannels::GetPrevNormal(int Idx) void cChannels::ReNumber( void ) { - channelsHashSid.Clear(); + ClearChannelHashes(); int Number = 1; for (cChannel *channel = First(); channel; channel = Next(channel)) { if (channel->GroupSep()) { --- ../vdr-1.5.12-orig/nit.c 2007-08-17 16:02:45.000000000 +0200 +++ nit.c 2007-12-28 22:58:21.000000000 +0100 @@ -142,20 +142,19 @@ void cNitFilter::Process(u_short Pid, u_ } if (Setup.UpdateChannels >= 5) { bool found = false; - for (cChannel *Channel = Channels.First(); Channel; Channel = Channels.Next(Channel)) { - if (!Channel->GroupSep() && Channel->Source() == Source && Channel->Nid() == ts.getOriginalNetworkId() && Channel->Tid() == ts.getTransportStreamId()) { - int transponder = Channel->Transponder(); - if (!ISTRANSPONDER(cChannel::Transponder(Frequency, Polarization), transponder)) { - for (int n = 0; n < NumFrequencies; n++) { - if (ISTRANSPONDER(cChannel::Transponder(Frequencies[n], Polarization), transponder)) { - Frequency = Frequencies[n]; - break; - } + cIterator<cChannel> ChannelIterator = Channels.GetChannelsBySourceNidTid(Source, ts.getOriginalNetworkId(), ts.getTransportStreamId()); + for (cChannel *Channel = ChannelIterator.First(); Channel; Channel = ChannelIterator.Next()) { + int transponder = Channel->Transponder(); + if (!ISTRANSPONDER(cChannel::Transponder(Frequency, Polarization), transponder)) { + for (int n = 0; n < NumFrequencies; n++) { + if (ISTRANSPONDER(cChannel::Transponder(Frequencies[n], Polarization), transponder)) { + Frequency = Frequencies[n]; + break; } - } - if (ISTRANSPONDER(cChannel::Transponder(Frequency, Polarization), Transponder())) // only modify channels if we're actually receiving this transponder - Channel->SetSatTransponderData(Source, Frequency, Polarization, SymbolRate, CodeRate); + } } + if (ISTRANSPONDER(cChannel::Transponder(Frequency, Polarization), Transponder())) // only modify channels if we're actually receiving this transponder + Channel->SetSatTransponderData(Source, Frequency, Polarization, SymbolRate, CodeRate); found = true; } if (!found) { @@ -193,20 +192,19 @@ void cNitFilter::Process(u_short Pid, u_ } if (Setup.UpdateChannels >= 5) { bool found = false; - for (cChannel *Channel = Channels.First(); Channel; Channel = Channels.Next(Channel)) { - if (!Channel->GroupSep() && Channel->Source() == Source && Channel->Nid() == ts.getOriginalNetworkId() && Channel->Tid() == ts.getTransportStreamId()) { - int transponder = Channel->Transponder(); - if (!ISTRANSPONDER(Frequency / 1000, transponder)) { - for (int n = 0; n < NumFrequencies; n++) { - if (ISTRANSPONDER(Frequencies[n] / 1000, transponder)) { - Frequency = Frequencies[n]; - break; - } + cIterator<cChannel> ChannelIterator = Channels.GetChannelsBySourceNidTid(Source, ts.getOriginalNetworkId(), ts.getTransportStreamId()); + for (cChannel *Channel = ChannelIterator.First(); Channel; Channel = ChannelIterator.Next()) { + int transponder = Channel->Transponder(); + if (!ISTRANSPONDER(Frequency / 1000, transponder)) { + for (int n = 0; n < NumFrequencies; n++) { + if (ISTRANSPONDER(Frequencies[n] / 1000, transponder)) { + Frequency = Frequencies[n]; + break; } - } - if (ISTRANSPONDER(Frequency / 1000, Transponder())) // only modify channels if we're actually receiving this transponder - Channel->SetCableTransponderData(Source, Frequency, Modulation, SymbolRate, CodeRate); + } } + if (ISTRANSPONDER(Frequency / 1000, Transponder())) // only modify channels if we're actually receiving this transponder + Channel->SetCableTransponderData(Source, Frequency, Modulation, SymbolRate, CodeRate); found = true; } if (!found) { @@ -251,32 +249,31 @@ void cNitFilter::Process(u_short Pid, u_ } if (Setup.UpdateChannels >= 5) { bool found = false; - for (cChannel *Channel = Channels.First(); Channel; Channel = Channels.Next(Channel)) { - if (!Channel->GroupSep() && Channel->Source() == Source && Channel->Nid() == ts.getOriginalNetworkId() && Channel->Tid() == ts.getTransportStreamId()) { - int transponder = Channel->Transponder(); - if (!ISTRANSPONDER(Frequency / 1000000, transponder)) { - for (int n = 0; n < NumFrequencies; n++) { - if (ISTRANSPONDER(Frequencies[n] / 1000000, transponder)) { - Frequency = Frequencies[n]; - break; - } + cIterator<cChannel> ChannelIterator = Channels.GetChannelsBySourceNidTid(Source, ts.getOriginalNetworkId(), ts.getTransportStreamId()); + for (cChannel *Channel = ChannelIterator.First(); Channel; Channel = ChannelIterator.Next()) { + int transponder = Channel->Transponder(); + if (!ISTRANSPONDER(Frequency / 1000000, transponder)) { + for (int n = 0; n < NumFrequencies; n++) { + if (ISTRANSPONDER(Frequencies[n] / 1000000, transponder)) { + Frequency = Frequencies[n]; + break; } - } - if (ISTRANSPONDER(Frequency / 1000000, Transponder())) // only modify channels if we're actually receiving this transponder - Channel->SetTerrTransponderData(Source, Frequency, Bandwidth, Constellation, Hierarchy, CodeRateHP, CodeRateLP, GuardInterval, TransmissionMode); + } } + if (ISTRANSPONDER(Frequency / 1000000, Transponder())) // only modify channels if we're actually receiving this transponder + Channel->SetTerrTransponderData(Source, Frequency, Bandwidth, Constellation, Hierarchy, CodeRateHP, CodeRateLP, GuardInterval, TransmissionMode); found = true; } if (!found) { for (int n = 0; n < NumFrequencies; n++) { cChannel *Channel = new cChannel; Channel->SetId(ts.getOriginalNetworkId(), ts.getTransportStreamId(), 0, 0); - if (Channel->SetTerrTransponderData(Source, Frequencies[n], Bandwidth, Constellation, Hierarchy, CodeRateHP, CodeRateLP, GuardInterval, TransmissionMode)) + if (Channel->SetTerrTransponderData(Source, Frequencies[n], Bandwidth, Constellation, Hierarchy, CodeRateHP, CodeRateLP, GuardInterval, TransmissionMode, Alpha, Priority)) EITScanner.AddTransponder(Channel); else delete Channel; } - } + } } } break;
--- ../vdr-1.5.12-orig/skinclassic.c 2007-07-29 14:35:03.000000000 +0200 +++ skinclassic.c 2007-12-28 22:33:42.000000000 +0100 @@ -77,6 +77,7 @@ private: int lineHeight; int timeWidth; bool message; + cString lastDate; public: cSkinClassicDisplayChannel(bool WithInfo); virtual ~cSkinClassicDisplayChannel(); @@ -149,7 +150,10 @@ void cSkinClassicDisplayChannel::Flush(v cString date = DayDateTime(); const cFont *font = cFont::GetFont(fontSml); int w = font->Width(date); - osd->DrawText(osd->Width() - w - 2, 0, date, Theme.Color(clrChannelDate), Theme.Color(clrBackground), cFont::GetFont(fontSml), w); + if (!lastDate || strcmp(lastDate, date)) { + osd->DrawText(osd->Width() - w - 2, 0, date, Theme.Color(clrChannelDate), Theme.Color(clrBackground), cFont::GetFont(fontSml), w); + lastDate = date; + } } osd->Flush(); } --- ../vdr-1.5.12-orig/skinsttng.c 2007-06-17 15:51:56.000000000 +0200 +++ skinsttng.c 2007-12-28 22:38:59.000000000 +0100 @@ -131,6 +131,7 @@ private: const cEvent *present; int lastSeen; tTrackId lastTrackId; + cString lastDate; static cBitmap bmTeletext, bmRadio, bmAudio, bmDolbyDigital, bmEncrypted, bmRecording; public: cSkinSTTNGDisplayChannel(bool WithInfo); @@ -307,7 +308,11 @@ void cSkinSTTNGDisplayChannel::Flush(voi const cFont *font = cFont::GetFont(fontSml); cString date = DayDateTime(); int w = font->Width(date); - osd->DrawText(x4 - w - 2, y7 - font->Height(), date, Theme.Color(clrChannelDate), frameColor, font, w); + if (!lastDate || strcmp(lastDate, date)) { + osd->DrawText(x4 - w - 2, y7 - font->Height(), date, Theme.Color(clrChannelDate), frameColor, font, w); + lastDate = date; + } + cDevice *Device = cDevice::PrimaryDevice(); const tTrackId *Track = Device->GetTrack(Device->GetCurrentAudioTrack()); if (!Track && *lastTrackId.description || Track && strcmp(lastTrackId.description, Track->description)) {
--- ../vdr-1.5.12-orig/osd.h 2007-10-12 16:28:44.000000000 +0200 +++ osd.h 2007-12-26 22:49:13.000000000 +0100 @@ -135,6 +135,7 @@ private: int x0, y0; int width, height; int dirtyX1, dirtyY1, dirtyX2, dirtyY2; + void SetIndexInternal(int x, int y, tIndex Index); public: cBitmap(int Width, int Height, int Bpp, int X0 = 0, int Y0 = 0); ///< Creates a bitmap with the given Width, Height and color depth (Bpp). --- ../vdr-1.5.12-orig/osd.c 2007-10-12 14:38:36.000000000 +0200 +++ osd.c 2007-12-28 10:21:35.000000000 +0100 @@ -394,15 +394,20 @@ bool cBitmap::SetXpm(const char *const X void cBitmap::SetIndex(int x, int y, tIndex Index) { if (bitmap) { - if (0 <= x && x < width && 0 <= y && y < height) { - if (bitmap[width * y + x] != Index) { - bitmap[width * y + x] = Index; - if (dirtyX1 > x) dirtyX1 = x; - if (dirtyY1 > y) dirtyY1 = y; - if (dirtyX2 < x) dirtyX2 = x; - if (dirtyY2 < y) dirtyY2 = y; - } - } + if (0 <= x && x < width && 0 <= y && y < height) + SetIndexInternal(x, y, Index); + } +} + +void cBitmap::SetIndexInternal(int x, int y, tIndex Index) +{ + // this function relies on existing bitmap and valid coordinates + if (bitmap[width * y + x] != Index) { + bitmap[width * y + x] = Index; + if (dirtyX1 > x) dirtyX1 = x; + if (dirtyY1 > y) dirtyY1 = y; + if (dirtyX2 < x) dirtyX2 = x; + if (dirtyY2 < y) dirtyY2 = y; } } @@ -410,37 +415,147 @@ void cBitmap::DrawPixel(int x, int y, tC { x -= x0; y -= y0; - if (0 <= x && x < width && 0 <= y && y < height) - SetIndex(x, y, Index(Color)); + if (bitmap && 0 <= x && x < width && 0 <= y && y < height) + SetIndexInternal(x, y, Index(Color)); } void cBitmap::DrawBitmap(int x, int y, const cBitmap &Bitmap, tColor ColorFg, tColor ColorBg, bool ReplacePalette, bool Overlay) { if (bitmap && Bitmap.bitmap && Intersects(x, y, x + Bitmap.Width() - 1, y + Bitmap.Height() - 1)) { - if (Covers(x, y, x + Bitmap.Width() - 1, y + Bitmap.Height() - 1)) + bool Covered = Covers(x, y, x + Bitmap.Width() - 1, y + Bitmap.Height() - 1); + if (Covered) Reset(); x -= x0; y -= y0; - if (ReplacePalette && Covers(x + x0, y + y0, x + x0 + Bitmap.Width() - 1, y + y0 + Bitmap.Height() - 1)) { + // determine valid destination area [x1,x2]x[y1,y2] to avoid range checks inside the loops + int x1 = max(0, x), x2 = min(0 + width , x + Bitmap.width) - 1; + int y1 = max(0, y), y2 = min(0 + height, y + Bitmap.height) - 1; + +#define FOR_Y_LOOP0 \ + tIndex *pRowSrc = &Bitmap.bitmap[Bitmap.width * (y1 - y) + (x1 - x)]; \ + tIndex *pRowDst = &bitmap[width * y1 + x1]; \ + for (int &yy = y1, ye = min(y2, dirtyY1 - 1); yy <= ye; yy++, pRowDst += width, pRowSrc += Bitmap.width) + +#define FOR_Y_LOOP1 \ + tIndex *pRowSrc = &Bitmap.bitmap[Bitmap.width * (y2 - y) + (x1 - x)]; \ + tIndex *pRowDst = &bitmap[width * y2 + x1]; \ + for (int &yy = y2, ye = max(y1, dirtyY2 + 1); yy >= ye; yy--, pRowDst -= width, pRowSrc -= Bitmap.width) + +#define DETECT_DIRTY_AREA_Y(Reverse, TransferCondition, TransferOperation) \ + do { \ + FOR_Y_LOOP##Reverse { \ + tIndex *pSrc = pRowSrc; \ + tIndex *pDst = pRowDst; \ + bool GotDirty = false; \ + for (int xx = x1; xx <= x2; xx++) { \ + if (TransferCondition) { \ + if (*pDst != TransferOperation) { \ + GotDirty = true; \ + if (dirtyX1 > xx) dirtyX1 = xx; \ + if (dirtyX2 < xx) dirtyX2 = xx; \ + } \ + } \ + pSrc++; \ + pDst++; \ + } \ + if (GotDirty) { \ + if (dirtyY1 > yy) dirtyY1 = yy; \ + if (dirtyY2 < yy) dirtyY2 = yy; \ + break; \ + } \ + } \ + } \ + while (false) + +#define FOR_X_LOOP0 \ + tIndex *pColSrc = &Bitmap.bitmap[Bitmap.width * (y1 - y) + (x1 - x)]; \ + tIndex *pColDst = &bitmap[width * y1 + x1]; \ + for (int &xx = x1, xe = min(x2, dirtyX1 - 1); xx <= xe; xx++, pColDst++, pColSrc++) + +#define FOR_X_LOOP1 \ + tIndex *pColSrc = &Bitmap.bitmap[Bitmap.width * (y1 - y) + (x2 - x)]; \ + tIndex *pColDst = &bitmap[width * y1 + x2]; \ + for (int &xx = x2, xe = max(x1, dirtyX2 + 1); xx >= xe; xx--, pColDst--, pColSrc--) + +#define DETECT_DIRTY_AREA_X(Reverse, TransferCondition, TransferOperation) \ + do { \ + FOR_X_LOOP##Reverse { \ + tIndex *pSrc = pColSrc; \ + tIndex *pDst = pColDst; \ + bool GotDirty = false; \ + for (int yy = y1; yy <= y2; yy++) { \ + if (TransferCondition) { \ + if (*pDst != TransferOperation) { \ + GotDirty = true; \ + if (dirtyX1 > xx) dirtyX1 = xx; \ + if (dirtyX2 < xx) dirtyX2 = xx; \ + break; \ + } \ + } \ + pSrc += Bitmap.width; \ + pDst += width; \ + } \ + if (GotDirty) \ + break; \ + } \ + } \ + while (false) + +#define DRAW_BITMAP(TransferCondition, TransferOperation, CanUseMemCpy) \ + do { \ + DETECT_DIRTY_AREA_Y(0, TransferCondition, TransferOperation); /* above */ \ + DETECT_DIRTY_AREA_Y(1, TransferCondition, TransferOperation); /* below */ \ + if (y2 < y1) /* nothing dirty */ \ + return; \ + DETECT_DIRTY_AREA_X(0, TransferCondition, TransferOperation); /* left */ \ + DETECT_DIRTY_AREA_X(1, TransferCondition, TransferOperation); /* right */ \ + /* process dirty area now */ \ + tIndex *pRowSrc = &Bitmap.bitmap[Bitmap.width * (y1 - y) + (x1 - x)]; \ + tIndex *pRowDst = &bitmap[width * y1 + x1]; \ + int n = sizeof(tIndex) * (x2 - x1 + 1); \ + for (int yy = y1; yy <= y2; yy++) { \ + tIndex *pSrc = pRowSrc; \ + tIndex *pDst = pRowDst; \ + if (CanUseMemCpy) \ + memcpy(pDst, pSrc, n); \ + else { \ + for (int xx = x1; xx <= x2; xx++) { \ + if (TransferCondition) \ + *pDst = TransferOperation; \ + pSrc++; \ + pDst++; \ + } \ + } \ + pRowSrc += Bitmap.width; \ + pRowDst += width; \ + } \ + } \ + while (false) + + if (ReplacePalette && Covered) { Replace(Bitmap); - for (int ix = 0; ix < Bitmap.width; ix++) { - for (int iy = 0; iy < Bitmap.height; iy++) { - if (!Overlay || Bitmap.bitmap[Bitmap.width * iy + ix] != 0) - SetIndex(x + ix, y + iy, Bitmap.bitmap[Bitmap.width * iy + ix]); - } - } + if (Overlay) + DRAW_BITMAP(*pSrc != 0, *pSrc, false); + else + DRAW_BITMAP(true, *pSrc, true); } else { tIndexes Indexes; Take(Bitmap, &Indexes, ColorFg, ColorBg); - for (int ix = 0; ix < Bitmap.width; ix++) { - for (int iy = 0; iy < Bitmap.height; iy++) { - if (!Overlay || Bitmap.bitmap[Bitmap.width * iy + ix] != 0) - SetIndex(x + ix, y + iy, Indexes[int(Bitmap.bitmap[Bitmap.width * iy + ix])]); - } - } + if (Overlay) + DRAW_BITMAP(*pSrc != 0, Indexes[(int)*pSrc], false); + else + DRAW_BITMAP(true, Indexes[(int)*pSrc], false); } } + +#undef DRAW_BITMAP +#undef DETECT_DIRTY_AREA_Y +#undef FOR_Y_LOOP0 +#undef FOR_Y_LOOP1 +#undef DETECT_DIRTY_AREA_X +#undef FOR_X_LOOP0 +#undef FOR_X_LOOP1 } void cBitmap::DrawText(int x, int y, const char *s, tColor ColorFg, tColor ColorBg, const cFont *Font, int Width, int Height, int Alignment) @@ -502,10 +617,91 @@ void cBitmap::DrawRectangle(int x1, int x2 = min(x2, width - 1); y2 = min(y2, height - 1); tIndex c = Index(Color); - for (int y = y1; y <= y2; y++) - for (int x = x1; x <= x2; x++) - SetIndex(x, y, c); + +#define FOR_Y_LOOP0 \ + tIndex *pRowDst = &bitmap[width * y1 + x1]; \ + for (int &yy = y1, ye = min(y2, dirtyY1 - 1); yy <= ye; yy++, pRowDst += width) + +#define FOR_Y_LOOP1 \ + tIndex *pRowDst = &bitmap[width * y2 + x1]; \ + for (int &yy = y2, ye = max(y1, dirtyY2 + 1); yy >= ye; yy--, pRowDst -= width) + +#define DETECT_DIRTY_AREA_Y(Reverse) \ + do { \ + FOR_Y_LOOP##Reverse { \ + tIndex *pDst = pRowDst; \ + bool GotDirty = false; \ + for (int xx = x1; xx <= x2; xx++) { \ + if (*pDst != c) { \ + GotDirty = true; \ + if (dirtyX1 > xx) dirtyX1 = xx; \ + if (dirtyX2 < xx) dirtyX2 = xx; \ + } \ + pDst++; \ + } \ + if (GotDirty) { \ + if (dirtyY1 > yy) dirtyY1 = yy; \ + if (dirtyY2 < yy) dirtyY2 = yy; \ + break; \ + } \ + } \ + } \ + while (false) + +#define FOR_X_LOOP0 \ + tIndex *pColDst = &bitmap[width * y1 + x1]; \ + for (int &xx = x1, xe = min(x2, dirtyX1 - 1); xx <= xe; xx++, pColDst++) + +#define FOR_X_LOOP1 \ + tIndex *pColDst = &bitmap[width * y1 + x2]; \ + for (int &xx = x2, xe = max(x1, dirtyX2 + 1); xx >= xe; xx--, pColDst--) + +#define DETECT_DIRTY_AREA_X(Reverse) \ + do { \ + FOR_X_LOOP##Reverse { \ + tIndex *pDst = pColDst; \ + bool GotDirty = false; \ + for (int yy = y1; yy <= y2; yy++) { \ + if (*pDst != c) { \ + GotDirty = true; \ + if (dirtyX1 > xx) dirtyX1 = xx; \ + if (dirtyX2 < xx) dirtyX2 = xx; \ + break; \ + } \ + pDst += width; \ + } \ + if (GotDirty) \ + break; \ + } \ + } \ + while (false) + + DETECT_DIRTY_AREA_Y(0); /* above */ + DETECT_DIRTY_AREA_Y(1); /* below */ + if (y2 < y1) /* nothing dirty */ + return; + DETECT_DIRTY_AREA_X(0); /* left */ + DETECT_DIRTY_AREA_X(1); /* right */ + // now fill only dirty area of rectangle + tIndex *pRowDst = &bitmap[width * y1 + x1]; + tIndex *pDst = pRowDst; + for (int x = x1; x <= x2; x++) + *pDst++ = c; + // copy the single line above to all other lines + tIndex *pRowSrc = pRowDst; + int n = sizeof(tIndex) * (x2 - x1 + 1); + for (int y = y1 + 1; y <= y2; y++) { + pRowDst += width; + memcpy(pRowDst, pRowSrc, n); + } } + +#undef DETECT_DIRTY_AREA_Y +#undef FOR_Y_LOOP0 +#undef FOR_Y_LOOP1 +#undef DETECT_DIRTY_AREA_X +#undef FOR_X_LOOP0 +#undef FOR_X_LOOP1 } void cBitmap::DrawEllipse(int x1, int y1, int x2, int y2, tColor Color, int Quadrants)
_______________________________________________ vdr mailing list vdr@xxxxxxxxxxx http://www.linuxtv.org/cgi-bin/mailman/listinfo/vdr