diff --git a/include/lgi/common/AudioView.h b/include/lgi/common/AudioView.h --- a/include/lgi/common/AudioView.h +++ b/include/lgi/common/AudioView.h @@ -1,977 +1,982 @@ /// Audio waveform display control #pragma once #include "lgi/common/Layout.h" #include "lgi/common/DisplayString.h" #include "lgi/common/Menu.h" #include "lgi/common/ClipBoard.h" #include "lgi/common/FileSelect.h" #define CC(code) LgiSwap32(code) struct int24_t { int32_t s : 24; }; union sample_t { int8_t *i8; int16_t *i16; int24_t *i24; int32_t *i32; float *f; sample_t(void *ptr = NULL) { *this = ptr; } sample_t &operator=(void *ptr) { i8 = (int8_t*)ptr; return *this; } }; typedef int32_t (*ConvertFn)(sample_t &ptr); class LAudioView : public LLayout { public: enum LFileType { AudioUnknown, AudioRaw, AudioWav, }; enum LSampleType { AudioS8, AudioS16LE, AudioS16BE, AudioS24LE, AudioS24BE, AudioS32LE, AudioS32BE, AudioFloat32, }; protected: enum LCmds { IDC_COPY_CURSOR, IDC_LOAD_WAV, IDC_LOAD_RAW, IDC_SAVE_WAV, IDC_SAVE_RAW, IDC_DRAW_AUTO, IDC_DRAW_LINE, IDC_SCAN_SAMPLES, IDC_SCAN_GROUPS, IDC_44K, IDC_48K, IDC_88K, IDC_96K, IDC_MONO, IDC_STEREO, IDC_2pt1_CHANNELS, IDC_5pt1_CHANNELS, }; enum LDrawMode { DrawAutoSelect, DrawSamples, ScanSamples, ScanGroups, }; LArray Audio; LFileType Type = AudioUnknown; LSampleType SampleType = AudioS16LE; int SampleBits = 0; int SampleRate = 0; int Channels = 0; size_t DataStart = 0; size_t CursorSample = 0; LRect Data; LArray ChData; double XZoom = 1.0; LString Msg, ErrorMsg; LDrawMode DrawMode = DrawAutoSelect; template struct Grp { T Min = 0, Max = 0; }; LArray>> IntGrps; LArray>> FloatGrps; void MouseToCursor(LMouse &m) { auto idx = ViewToSample(m.x); if (idx != CursorSample) { CursorSample = idx; UpdateMsg(); Invalidate(); } } void SetData(LRect &r) { Data = r; ChData.Length(Channels); int Dy = Data.Y() - 1; for (int i=0; i 0 && rd != f.GetSize()) Audio.Length(rd); auto Ext = LGetExtension(FileName); if (!Stricmp(Ext, "wav")) Type = AudioWav; else if (!Stricmp(Ext, "raw")) Type = AudioRaw; else { ErrorMsg.Printf("Unknown format: %s", Ext); return Empty(); } if (rate) SampleRate = rate; DataStart = 0; SampleBits = bitDepth; Channels = channels; if (bitDepth == 16) { SampleType = AudioS16LE; } else if (bitDepth == 32) { SampleType = AudioS32LE; } else if (Type == AudioWav) { // Parse the wave file... LPointer p; p.s8 = Audio.AddressOf(); auto end = p.s8 + Audio.Length(); if (*p.u32++ != CC('RIFF')) return Empty(); auto ChunkSz = *p.u32++; auto Fmt = *p.u32++; while (p.s8 < end) { auto SubChunkId = *p.u32++; auto SubChunkSz = *p.u32++; auto NextChunk = p.u8 + SubChunkSz; if (SubChunkId == CC('fmt ')) { auto AudioFmt = *p.u16++; Channels = *p.u16++; SampleRate = *p.u32++; auto ByteRate = *p.u32++; auto BlockAlign = *p.u16++; SampleBits = *p.u16++; if (SampleBits == 16) SampleType = AudioS16LE; else if (SampleBits == 24) SampleType = AudioS24LE; else if (SampleBits == 32) SampleType = AudioS32LE; printf("Channels=%i\n", Channels); printf("SampleRate=%i\n", SampleRate); printf("SampleBits=%i\n", SampleBits); } else if (SubChunkId == CC('data')) { DataStart = p.s8 - Audio.AddressOf(); printf("DataStart=" LPrintfSizeT "\n", DataStart); break; } p.u8 = NextChunk; } if (!DataStart) { ErrorMsg = "No 'data' element found."; return Empty(); } } else if (Type == AudioRaw) { // Assume 32bit SampleType = AudioS32LE; SampleBits = 32; } if (!SampleRate) SampleRate = 44100; if (!Channels) Channels = 2; Invalidate(); return true; } LRect DefaultPos() { auto c = GetClient(); c.Inset(20, 20); return c; } size_t GetSamples() { return (Audio.Length() - DataStart) / (SampleBits >> 3) / Channels; } int SampleToView(size_t idx) { return Data.x1 + (int)((idx * Data.X()) / GetSamples()); } size_t ViewToSample(int x /*px*/) { int offset = x - Data.x1; ssize_t samples = GetSamples(); ssize_t idx = offset * samples / Data.X(); if (idx < 0) idx = 0; else if (idx >= samples) idx = samples - 1; return idx; } void OnPosChange() { auto def = DefaultPos(); LRect d = Data; d.y1 = def.y1; d.y2 = def.y2; SetData(d); } #define CheckItem(Sub, Name, Id, Chk) \ { \ auto item = Sub->AppendItem(Name, Id); \ if (item && Chk) item->Checked(true); \ } void OnMouseClick(LMouse &m) { if (m.IsContextMenu()) { LSubMenu s; s.AppendItem("Copy Cursor Address", IDC_COPY_CURSOR); s.AppendSeparator(); auto File = s.AppendSub("File"); File->AppendItem("Load Wav", IDC_LOAD_WAV); File->AppendItem("Load Raw", IDC_LOAD_RAW); File->AppendItem("Save Wav", IDC_SAVE_WAV); File->AppendItem("Save Raw", IDC_SAVE_RAW); auto Draw = s.AppendSub("Draw Mode"); CheckItem(Draw, "Auto", IDC_DRAW_AUTO, DrawMode == DrawAutoSelect); CheckItem(Draw, "Line", IDC_DRAW_LINE, DrawMode == DrawSamples); CheckItem(Draw, "Scan Samples", IDC_SCAN_SAMPLES, DrawMode == ScanSamples); CheckItem(Draw, "Scan Groups", IDC_SCAN_GROUPS, DrawMode == ScanGroups); auto Rate = s.AppendSub("Sample Rate"); CheckItem(Rate, "44.1k", IDC_44K, SampleRate == 44100); CheckItem(Rate, "48k", IDC_48K, SampleRate == 4800); CheckItem(Rate, "88.2k", IDC_88K, SampleRate == 44100*2); CheckItem(Rate, "96k", IDC_96K, SampleRate == 48000*2); auto Ch = s.AppendSub("Channels"); CheckItem(Ch, "Mono", IDC_MONO, Channels == 1); CheckItem(Ch, "Stereo", IDC_STEREO, Channels == 2); CheckItem(Ch, "2.1", IDC_2pt1_CHANNELS, Channels == 3); CheckItem(Ch, "5.1", IDC_5pt1_CHANNELS, Channels == 6); bool Update = false; switch (s.Float(this, m)) { case IDC_COPY_CURSOR: { LClipBoard clip(this); size_t addrVal = DataStart + (CursorSample * Channels * SampleBits / 8); LString addr; addr.Printf(LPrintfSizeT, addrVal); clip.Text(addr); break; } case IDC_SAVE_RAW: { auto s = new LFileSelect; s->Parent(this); s->Save([this](auto s, auto ok) { if (ok) { LFile out(s->Name(), O_WRITE); if (out) { out.SetSize(0); auto ptr = Audio.AddressOf(DataStart); out.Write(ptr, Audio.Length() - DataStart); } else LgiMsg(this, "Can't open '%s' for writing.", "Error", MB_OK, s->Name()); } delete s; }); break; } case IDC_DRAW_AUTO: DrawMode = DrawAutoSelect; Update = true; break; case IDC_DRAW_LINE: DrawMode = DrawSamples; Update = true; break; case IDC_SCAN_SAMPLES: DrawMode = ScanSamples; Update = true; break; case IDC_SCAN_GROUPS: DrawMode = ScanGroups; Update = true; break; case IDC_44K: SampleRate = 44100; Update = true; break; case IDC_48K: SampleRate = 48000; Update = true; break; case IDC_88K: SampleRate = 44100*2; Update = true; break; case IDC_96K: SampleRate = 48000*2; Update = true; break; case IDC_MONO: Channels = 1; Update = true; break; case IDC_STEREO: Channels = 2; Update = true; break; case IDC_2pt1_CHANNELS: Channels = 3; Update = true; break; case IDC_5pt1_CHANNELS: Channels = 6; Update = true; break; default: break; } if (Update) { IntGrps.Length(0); FloatGrps.Length(0); SetData(Data); Invalidate(); } } else if (m.Left()) { Focus(true); MouseToCursor(m); } } void OnMouseMove(LMouse &m) { if (m.Down()) MouseToCursor(m); } LPointer AddressOf(size_t Sample) { LPointer p; int SampleBytes = SampleBits >> 3; p.s8 = Audio.AddressOf(DataStart + (Sample * Channels * SampleBytes)); return p; } void UpdateMsg() { ssize_t Samples = GetSamples(); auto Addr = AddressOf(CursorSample); size_t AddrOffset = Addr.s8 - Audio.AddressOf(); LString::Array Val; size_t GraphLen = 0; for (int ch=0; ch int32_t { return *s.i8++; }; case AudioS16LE: return [](sample_t &s) -> int32_t { return *s.i16++; }; case AudioS16BE: return [](sample_t &s) -> int32_t { int16_t i = LgiSwap16(*s.i16); s.i16++; return i; }; case AudioS24LE: return [](sample_t &s) -> int32_t { int32_t i = s.i24->s; s.i8 += 3; return i; }; case AudioS24BE: return [](sample_t &s) -> int32_t { int24_t i; int8_t *p = (int8_t*)&i; p[0] = s.i8[2]; p[1] = s.i8[1]; p[2] = s.i8[0]; s.i8 += 3; return i.s; }; case AudioS32LE: return [](sample_t &s) -> int32_t { return *s.i32++; }; case AudioS32BE: return [](sample_t &s) -> int32_t { int32_t i = *s.i32++; return LgiSwap32(i); }; } return NULL; } void PaintSamples(LSurface *pDC, int ChannelIdx, LArray>> *Grps) { #if PROFILE_PAINT_SAMPLES LProfile Prof("PaintSamples", 10); #endif int32_t MaxT = 0; ConvertFn Convert = GetConvert(); if (SampleBits == 8) MaxT = 0x7f; else if (SampleBits == 16) MaxT = 0x7fff; else if (SampleBits == 24) MaxT = 0x7fffff; else MaxT = 0x7fffffff; auto SampleBytes = SampleBits / 8; auto Client = GetClient(); auto &c = ChData[ChannelIdx]; auto start = Audio.AddressOf(DataStart); size_t samples = GetSamples(); - printf("samples=" LPrintfSizeT "\n", samples); - auto end = start + (samples * Channels); + // printf("samples=" LPrintfSizeT "\n", samples); + auto end = start + (SampleBytes * samples * Channels); int cy = c.y1 + (c.Y() / 2); pDC->Colour(cGrid); pDC->Box(&c); int blk = 256; if (Grps->Length() == 0) { // Initialize the graph PROF("GraphGen"); Grps->Length(Channels); for (int ch = 0; ch < Channels; ch++) { auto &GraphArr = (*Grps)[ch]; GraphArr.SetFixedLength(false); GraphArr.Length(0); int BlkBytes = blk * Channels * SampleBytes; printf("BlkBytes=%i\n", BlkBytes); size_t BlkIndex = 0; int byteOffsetToSample = ch * SampleBytes; printf("byteOffsetToSample=%i\n", byteOffsetToSample); printf("SampleBytes=%i\n", SampleBytes); sample_t s; s.i8 = start+byteOffsetToSample; // For each block... for (; s.i8 < end; s.i8 += BlkBytes) { int remain = MIN( (int)(end - s.i8), BlkBytes ); auto &b = GraphArr[BlkIndex++]; // For all samples in the block... - int8_t *blkEnd = s.i8 + (remain * SampleBytes); + int8_t *blkStart = s.i8; + int8_t *blkEnd = s.i8 + remain; auto smp = s; b.Min = b.Max = Convert(smp); // init min/max with first sample while (smp.i8 < blkEnd) { auto n = Convert(smp); if (n < b.Min) b.Min = n; if (n > b.Max) b.Max = n; } - /* - printf("bucket=%i %i-%i\n", (int)BlkIndex-1, b.Min, b.Max); - if (BlkIndex > 100) - return; - */ + /* + if (BlkIndex % 1000 == 0) + printf("bucket=%i %i-%i\n", (int)BlkIndex-1, b.Min, b.Max); + */ } + + printf("end diff=%i\n", (int) (end - s.i8)); + printf("GraphArr.len=" LPrintfSizeT "\n", GraphArr.Length()); GraphArr.SetFixedLength(true); } } PROF("PrePaint"); int YScale = c.Y() / 2; int CursorX = SampleToView(CursorSample); double blkScale = Grps->Length() > 0 ? (double) (*Grps)[0].Length() / c.X() : 1.0; - printf("blkScale=%f blks/px\n", blkScale); + // printf("blkScale=%f blks/px\n", blkScale); LDrawMode EffectiveMode = DrawMode; auto StartSample = ViewToSample(Client.x1); auto EndSample = ViewToSample(Client.x2); auto SampleRange = EndSample - StartSample; if (EffectiveMode == DrawAutoSelect) { - if (SampleRange < (Client.X() << 2)) + if (SampleRange < Client.X() * 32) EffectiveMode = DrawSamples; else if (blkScale < 1.0) EffectiveMode = ScanSamples; else EffectiveMode = ScanGroups; } + // This is the bytes in channels we're not looking at + int byteInc = (Channels - 1) * SampleBytes; + + pDC->Colour(cGrid); + pDC->HLine(c.x1, c.x2, cy); + // LgiTrace("DrawRange: " LPrintfSizeT "->" LPrintfSizeT " (" LPrintfSizeT ") mode=%i\n", StartSample, EndSample, SampleRange, (int)EffectiveMode); if (EffectiveMode == DrawSamples) { - sample_t pSample; - pSample.i8 = start + (StartSample * Channels) + ChannelIdx; + sample_t pSample(start + (((StartSample * Channels) + ChannelIdx) * SampleBytes)); if (CursorX >= Client.x1 && CursorX <= Client.x2) { pDC->Colour(L_BLACK); pDC->VLine(CursorX, c.y1, c.y2); } - pDC->Colour(cGrid); - pDC->HLine(c.x1, c.x2, cy); - - // LgiTrace(" ptr=%i\n", (int)((char*)pSample-(char*)start)); // For all samples in the view space... pDC->Colour(cMax); LPoint Prev(-1, -1); - for (auto idx = StartSample; idx <= EndSample + 1; idx++, pSample.i8 += Channels) + for (auto idx = StartSample; idx <= EndSample + 1; idx++, pSample.i8 += byteInc) { auto n = Convert(pSample); auto x = SampleToView(idx); - auto y = (uint32_t) (cy - ((uint64_t)n * YScale / MaxT)); + auto y = (cy - ((int64_t)n * YScale / MaxT)); if (idx != StartSample) pDC->Line(Prev.x, Prev.y, (int)x, (int)y); Prev.Set((int)x, (int)y); } } else { // For all x coordinates in the view space.. for (int Vx=Client.x1; Vx<=Client.x2; Vx++) { if (Vx % 100 == 0) { PROF("Pixels"); } if (Vx < c.x1 || Vx > c.x2) continue; auto isCursor = CursorX == Vx; if (isCursor) { pDC->Colour(L_BLACK); pDC->VLine(Vx, c.y1, c.y2); } Grp Min, Max; StartSample = ViewToSample(Vx); EndSample = ViewToSample(Vx+1); // printf("SampleIdx: " LPrintfSizeT "-" LPrintfSizeT "\n", StartSample, EndSample); if (EffectiveMode == ScanSamples) { // Scan individual samples - sample_t pStart; - pStart.i8 = start + (StartSample * Channels) + ChannelIdx; - auto pEnd = start + (EndSample * Channels) + ChannelIdx; - + sample_t pStart(start + (StartSample * Channels * SampleBytes) + ChannelIdx); + auto pEnd = start + (EndSample * Channels * SampleBytes) + ChannelIdx; + #if 0 if (Vx==Client.x1) { auto PosX = SampleToView((pStart - ChannelIdx - start) / Channels); LgiTrace(" ptr=%i " LPrintfSizeT "-" LPrintfSizeT " x=%i pos=%i\n", (int)((char*)pStart-(char*)start), StartSample, EndSample, Vx, PosX); } #endif - for (auto i = pStart; i.i8 < pEnd; i.i8 += Channels) + for (auto i = pStart; i.i8 < pEnd; i.i8 += byteInc) { auto n = Convert(i); - Min.Min = MIN(Min.Min, n); + Max.Min = MIN(Max.Min, n); Max.Max = MAX(Max.Max, n); } } else { // Scan the buckets auto &Graph = (*Grps)[ChannelIdx]; double blkStart = (double)StartSample * Graph.Length() / samples; double blkEnd = (double)EndSample * Graph.Length() / samples; + /* if (Vx % 10 == 0) printf("BlkRange[%i]: %g-%g of:%i\n", Vx, blkStart, blkEnd, (int)Graph.Length()); + */ #if 0 if (isCursor) LgiTrace("Blk %g-%g of " LPrintfSizeT "\n", blkStart, blkEnd, Graph.Length()); #endif auto iEnd = (int)ceil(blkEnd); int count = 0; for (int i=(int)floor(blkStart); iColour(isCursor ? cCursorMax : cMax); pDC->VLine(Vx, (int)y1, (int)y2); if (EffectiveMode == ScanGroups) { y1 = (cy - (Min.Max * YScale / MaxT)); y2 = (cy - (Min.Min * YScale / MaxT)); pDC->Colour(isCursor ? cCursorMin : cMin); pDC->VLine(Vx, (int)y1, (int)y2); } } } } void OnPaint(LSurface *pDC) { #ifdef WINDOWS LDoubleBuffer DblBuf(pDC); #endif pDC->Colour(L_WORKSPACE); pDC->Rectangle(); if (!Data.Valid()) { auto r = DefaultPos(); SetData(r); } auto Fnt = GetFont(); LDisplayString ds(Fnt, ErrorMsg ? ErrorMsg : Msg); Fnt->Transparent(true); Fnt->Fore(ErrorMsg ? LColour::Red : LColour(L_LOW)); ds.Draw(pDC, 4, 4); if (ErrorMsg) return; switch (SampleType) { case AudioS16LE: case AudioS16BE: { for (int ch = 0; ch < Channels; ch++) PaintSamples(pDC, ch, &IntGrps); break; } case AudioS24LE: case AudioS24BE: { for (int ch = 0; ch < Channels; ch++) PaintSamples(pDC, ch, &IntGrps); break; } case AudioS32LE: case AudioS32BE: { for (int ch = 0; ch < Channels; ch++) PaintSamples(pDC, ch, &IntGrps); break; } case AudioFloat32: { /* for (int ch = 0; ch < Channels; ch++) PaintSamples(pDC, ch, &FloatGrps); */ break; } } } bool UnitTests() { SampleType = AudioS16LE; auto c = GetConvert(); int16_t le16 = (2 << 8) | (1); sample_t ptr(&le16); auto out = c(ptr); if (out != 0x201) { LAssert(0); return false; } SampleType = AudioS16BE; c = GetConvert(); int16_t be16 = (1 << 8) | (2); ptr = &be16; out = c(ptr); if (out != 0x201) { LAssert(0); return false; } SampleType = AudioS24LE; c = GetConvert(); uint8_t le24[] = {1, 2, 3}; ptr = &le24; out = c(ptr); if (out != 0x30201) { LAssert(0); return false; } SampleType = AudioS24BE; c = GetConvert(); uint8_t be24[] = {3, 2, 1}; ptr = &be24; out = c(ptr); if (out != 0x30201) { LAssert(0); return false; } SampleType = AudioS32LE; c = GetConvert(); uint8_t le32[] = {1, 2, 3, 4}; ptr = &le32; out = c(ptr); if (out != 0x4030201) { LAssert(0); return false; } SampleType = AudioS32BE; c = GetConvert(); uint8_t be32[] = {4, 3, 2, 1}; ptr = &be32; out = c(ptr); if (out != 0x4030201) { LAssert(0); return false; } return true; } }; diff --git a/src/linux/Gtk/ScreenDC.cpp b/src/linux/Gtk/ScreenDC.cpp --- a/src/linux/Gtk/ScreenDC.cpp +++ b/src/linux/Gtk/ScreenDC.cpp @@ -1,604 +1,613 @@ /*hdr ** FILE: LScreenDC.cpp ** AUTHOR: Matthew Allen ** DATE: 14/10/2000 ** DESCRIPTION: GDC v2.xx header ** ** Copyright (C) 2000, Matthew Allen ** fret@memecode.com */ #include #include #include "lgi/common/Lgi.h" using namespace Gtk; class LScreenPrivate { public: int x, y, Bits; bool Own; LColour Col; LRect Client; LView *View; OsView v; OsDrawable *d; cairo_t *cr; cairo_matrix_t matrix; LScreenPrivate() { View = NULL; x = y = Bits = 0; Own = false; v = 0; d = NULL; cr = NULL; Client.ZOff(-1, -1); } ~LScreenPrivate() { } }; // Translates are cumulative... so we undo the last one before resetting it. ///////////////////////////////////////////////////////////////////////////////////////////////////// LScreenDC::LScreenDC() { d = new LScreenPrivate; d->x = GdcD->X(); d->y = GdcD->Y(); } LScreenDC::LScreenDC(int x, int y, int bits) { d = new LScreenPrivate; d->x = x; d->y = y; d->Bits = bits; } LScreenDC::LScreenDC(Gtk::cairo_t *cr, int x, int y) { d = new LScreenPrivate; d->Own = false; d->cr = cr; d->x = x; d->y = y; d->Bits = 32; ColourSpace = GdcD->GetColourSpace(); cairo_get_matrix(d->cr, &d->matrix); } LScreenDC::LScreenDC(OsDrawable *Drawable) { d = new LScreenPrivate; d->Own = false; d->d = Drawable; } LScreenDC::LScreenDC(LView *view, void *param) { d = new LScreenPrivate; d->View = view; if (view) { LWindow *w = view->GetWindow(); OsView v = w? GTK_WIDGET(w->WindowHandle()) : NULL; if (v) { d->v = v; LAssert(!"Gtk3 FIXME"); } else { d->x = view->X(); d->y = view->Y(); d->Bits = 0; d->Own = false; GdkScreen *s = gdk_display_get_default_screen(gdk_display_get_default()); if (s) { GdkVisual *v = gdk_screen_get_system_visual(s); if (v) { d->Bits = gdk_visual_get_depth(v); // d->Bits = v->depth; ColourSpace = GdkVisualToColourSpace(v, d->Bits); } } } } else { printf("%s:%i - No view?\n", _FL); } } LScreenDC::~LScreenDC() { DeleteObj(d); } OsPainter LScreenDC::Handle() { return d->cr; } LPoint LScreenDC::GetDpi() { return LScreenDpi(); } ::LString LScreenDC::Dump() { ::LString s; s.Printf("LScreenDC size=%i,%i\n", d->x, d->y); return s; } bool LScreenDC::SupportsAlphaCompositing() { // GTK/X11 doesn't seem to support alpha compositing. return false; } LPoint LScreenDC::GetSize() { return LPoint(d->x, d->y); } bool LScreenDC::GetClient(LRect *c) { if (!c) return false; *c = d->Client; return true; } void LScreenDC::GetOrigin(int &x, int &y) { if (d->Client.Valid()) { x = OriginX + d->Client.x1; y = OriginY + d->Client.y1; } else { x = OriginX; y = OriginY; } } void LScreenDC::SetOrigin(int x, int y) { if (d->Client.Valid()) { OriginX = x - d->Client.x1; OriginY = y - d->Client.y1; } else { OriginX = x; OriginY = y; } cairo_matrix_t m; cairo_get_matrix(d->cr, &m); m.x0 = d->matrix.x0 - OriginX; m.y0 = d->matrix.y0 - OriginY; cairo_set_matrix(d->cr, &m); } void LScreenDC::SetClient(LRect *c) { if (c) { d->Client = *c; cairo_save(d->cr); cairo_rectangle(d->cr, c->x1, c->y1, c->X(), c->Y()); cairo_clip(d->cr); cairo_translate(d->cr, c->x1, c->y1); OriginX = -c->x1; OriginY = -c->y1; } else { if (Clip.Valid()) ClipRgn(NULL); OriginX = 0; OriginY = 0; cairo_restore(d->cr); d->Client.ZOff(-1, -1); } } LRect *LScreenDC::GetClient() { return &d->Client; } uint LScreenDC::LineStyle() { return LSurface::LineStyle(); } uint LScreenDC::LineStyle(uint32_t Bits, uint32_t Reset) { return LSurface::LineStyle(Bits); } int LScreenDC::GetFlags() { return 0; } LRect LScreenDC::ClipRgn() { return Clip; } LRect LScreenDC::ClipRgn(LRect *c) { LRect Prev = Clip; if (c) { Clip = *c; // Don't add d->Client on here, as the translate makes it redundant. cairo_save(d->cr); cairo_new_path(d->cr); cairo_rectangle(d->cr, c->x1, c->y1, c->X(), c->Y()); cairo_clip(d->cr); } else { Clip.ZOff(-1, -1); cairo_restore(d->cr); } return Prev; } COLOUR LScreenDC::Colour() { return d->Col.Get(GetBits()); } COLOUR LScreenDC::Colour(COLOUR c, int Bits) { auto b = Bits?Bits:GetBits(); d->Col.Set(c, b); return Colour(d->Col).Get(b); } LColour LScreenDC::Colour(LColour c) { LColour Prev = d->Col; d->Col = c; if (d->cr) { double r = (double)d->Col.r()/255.0; double g = (double)d->Col.g()/255.0; double b = (double)d->Col.b()/255.0; double a = (double)d->Col.a()/255.0; cairo_set_source_rgba(d->cr, r, g, b, a); } return Prev; } int LScreenDC::Op(int ROP, NativeInt Param) { int Prev = Op(); switch (ROP) { case GDC_SET: { //d->p.setRasterOp(XPainter::CopyROP); break; } case GDC_OR: { //d->p.setRasterOp(XPainter::OrROP); break; } case GDC_AND: { //d->p.setRasterOp(XPainter::AndROP); break; } case GDC_XOR: { //d->p.setRasterOp(XPainter::XorROP); break; } } return Prev; } int LScreenDC::Op() { /* switch (d->p.rasterOp()) { case XPainter::CopyROP: { return GDC_SET; break; } case XPainter::OrROP: { return GDC_OR; break; } case XPainter::AndROP: { return GDC_AND; break; } case XPainter::XorROP: { return GDC_XOR; break; } } */ return GDC_SET; } int LScreenDC::X() { return d->Client.Valid() ? d->Client.X() : d->x; } int LScreenDC::Y() { return d->Client.Valid() ? d->Client.Y() : d->y; } int LScreenDC::GetBits() { return d->Bits; } LPalette *LScreenDC::Palette() { return 0; } void LScreenDC::Palette(LPalette *pPal, bool bOwnIt) { } void LScreenDC::Set(int x, int y) { cairo_rectangle(d->cr, x, y, 1, 1); cairo_fill(d->cr); } COLOUR LScreenDC::Get(int x, int y) { return 0; } void LScreenDC::HLine(int x1, int x2, int y) { + // cairo_rectangle is not reliable if the x1 and x2 are a + // long way off the edge of the screen. + if (x1 < 0) x1 = 0; + if (x2 >= d->x) x2 = d->x; + cairo_rectangle(d->cr, x1, y, x2-x1+1, 1); cairo_fill(d->cr); } void LScreenDC::VLine(int x, int y1, int y2) { + // stupid cairo_rectangle + if (y1 < 0) y1 = 0; + if (y2 >= d->y) y2 = d->y; + cairo_rectangle(d->cr, x, y1, 1, y2-y1+1); cairo_fill(d->cr); } void LScreenDC::Line(int x1, int y1, int x2, int y2) { cairo_move_to(d->cr, 0.5+x1, 0.5+y1); cairo_line_to (d->cr, 0.5+x2, 0.5+y2); cairo_set_line_width(d->cr, 1.0); cairo_set_line_cap(d->cr, CAIRO_LINE_CAP_SQUARE); cairo_stroke(d->cr); cairo_fill(d->cr); } void LScreenDC::Circle(double cx, double cy, double radius) { cairo_arc(d->cr, cx, cy, radius, 0, 2 * LGI_PI); cairo_stroke(d->cr); } void LScreenDC::FilledCircle(double cx, double cy, double radius) { cairo_arc(d->cr, cx, cy, radius, 0, 2 * LGI_PI); cairo_fill(d->cr); } void LScreenDC::Arc(double cx, double cy, double radius, double start, double end) { cairo_arc(d->cr, cx, cy, radius, start, end); cairo_stroke(d->cr); } void LScreenDC::FilledArc(double cx, double cy, double radius, double start, double end) { cairo_arc(d->cr, cx, cy, radius, start, end); cairo_fill(d->cr); } void LScreenDC::Ellipse(double cx, double cy, double x, double y) { cairo_save(d->cr); cairo_translate(d->cr, cx, cy); cairo_scale(d->cr, x, y); cairo_arc(d->cr, 0.0, 0.0, 1.0, 0.0, 2 * LGI_PI); // cairo_stroke(d->cr); cairo_restore(d->cr); } void LScreenDC::FilledEllipse(double cx, double cy, double x, double y) { cairo_save(d->cr); cairo_translate(d->cr, cx, cy); cairo_scale(d->cr, x, y); cairo_arc(d->cr, 0.0, 0.0, 1.0, 0.0, 2 * LGI_PI); cairo_fill(d->cr); cairo_restore(d->cr); } void LScreenDC::Box(int x1, int y1, int x2, int y2) { double w = x2 - x1 + 1; double h = y2 - y1 + 1; cairo_rectangle(d->cr, x1, y1, w, h); cairo_rectangle(d->cr, x1+1, y1+1, w-2, h-2); cairo_set_fill_rule(d->cr, CAIRO_FILL_RULE_EVEN_ODD); cairo_fill(d->cr); } void LScreenDC::Box(LRect *a) { if (a) Box(a->x1, a->y1, a->x2, a->y2); else Box(0, 0, X()-1, Y()-1); } void LScreenDC::Rectangle(int x1, int y1, int x2, int y2) { if (x2 >= x1 && y2 >= y1) { cairo_rectangle (d->cr, x1, y1, x2-x1+1, y2-y1+1); cairo_fill(d->cr); } } void LScreenDC::Rectangle(LRect *a) { if (a) { if (a->X() > 0 && a->Y() > 0) { cairo_rectangle (d->cr, a->x1, a->y1, a->X(), a->Y()); cairo_fill(d->cr); } } else { cairo_rectangle(d->cr, 0, 0, X(), Y()); cairo_fill(d->cr); } } void LScreenDC::Polygon(int Points, LPoint *Data) { if (!Data || Points <= 0) return; cairo_move_to(d->cr, Data[0].x, Data[0].y); for (int i=1; icr, Data[i].x, Data[i].y); cairo_close_path(d->cr); cairo_fill(d->cr); } void LScreenDC::Blt(int x, int y, LSurface *Src, LRect *a) { if (!Src) { LgiTrace("%s:%i - No source.\n", _FL); return; } if (Src->IsScreen()) { LgiTrace("%s:%i - Can't do screen->screen blt.\n", _FL); return; } // memory -> screen blt LRect RealClient = d->Client; d->Client.ZOff(-1, -1); // Clear this so the blit rgn calculation uses the // full context size rather than just the client. LBlitRegions br(this, x, y, Src, a); d->Client = RealClient; if (!br.Valid()) { // LgiTrace("Blt inval pos=%i,%i a=%s bounds=%s\n", x, y, a?a->GetStr():"null", Bounds().GetStr()); return; } LMemDC *Mem; if ((Mem = dynamic_cast(Src))) { auto Sub = Mem->GetSubImage(br.SrcClip); if (Sub) { cairo_pattern_t *Pat = cairo_pattern_create_for_surface(Sub); if (Pat) { /* { Gtk::cairo_matrix_t matrix; cairo_get_matrix(d->cr, &matrix); double ex[4]; cairo_clip_extents(d->cr, ex+0, ex+1, ex+2, ex+3); LgiTrace("LScreenDC::Blt xy=%i,%i clip=%g,%g+%g,%g cli=%s off=%i,%i\n", x, y, ex[0]+matrix.x0, ex[1]+ex[0], ex[2]+matrix.x0, ex[3]+ex[2], d->Client.GetStr(), OriginX, OriginY); } */ cairo_save(d->cr); cairo_translate(d->cr, br.DstClip.x1, br.DstClip.y1); cairo_set_source(d->cr, Pat); cairo_new_path(d->cr); cairo_rectangle(d->cr, 0, 0, br.DstClip.X(), br.DstClip.Y()); cairo_fill(d->cr); cairo_restore(d->cr); cairo_pattern_destroy(Pat); } } } } void LScreenDC::StretchBlt(LRect *d, LSurface *Src, LRect *s) { LAssert(0); } void LScreenDC::Bezier(int Threshold, LPoint *Pt) { LAssert(0); } void LScreenDC::FloodFill(int x, int y, int Mode, COLOUR Border, LRect *Bounds) { LAssert(0); }