Guest User

Untitled

a guest
Aug 29th, 2025
29
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 8.28 KB | None | 0 0
  1. // GifViewer.cpp — minimal animated GIF viewer for Haiku (giflib + BeAPI)
  2. // Fixes: create BMessageRunner in AttachedToWindow(), avoid unused-parameter warning,
  3. // clamp frame delay, recreate runner each frame.
  4.  
  5. #include <Application.h>
  6. #include <Bitmap.h>
  7. #include <Message.h>
  8. #include <MessageRunner.h>
  9. #include <Window.h>
  10. #include <View.h>
  11. #include <stdio.h>
  12. #include <string.h>
  13. #include <stdexcept>
  14.  
  15. extern "C" {
  16. #include <gif_lib.h>
  17. }
  18.  
  19. static const uint32 kMsgTick = 'tick';
  20.  
  21. // --- GifView -----------------------------------------------------------------
  22.  
  23. class GifView : public BView {
  24. public:
  25. GifView(BRect frame, GifFileType* gif)
  26. : BView(frame, "gifview", B_FOLLOW_ALL, B_WILL_DRAW | B_FRAME_EVENTS),
  27. fGif(gif),
  28. fCanvas(nullptr),
  29. fRunner(nullptr),
  30. fFrame(0),
  31. fLastDisposal(0),
  32. fHasLastRect(false),
  33. fLastDelayCS(10) // centiseconds (0.1s) default
  34. {
  35. SetViewColor(B_TRANSPARENT_32_BIT);
  36. _PrepareCanvas();
  37. _RenderFrame(0); // draw the first frame onto the canvas (no runner yet)
  38. }
  39.  
  40. ~GifView() override {
  41. delete fRunner;
  42. delete fCanvas;
  43. }
  44.  
  45. // create the runner only when attached (target messenger will be valid)
  46. void AttachedToWindow() override {
  47. BView::AttachedToWindow();
  48. _ScheduleNextTick();
  49. }
  50.  
  51. void DetachedFromWindow() override {
  52. delete fRunner;
  53. fRunner = nullptr;
  54. BView::DetachedFromWindow();
  55. }
  56.  
  57. void Draw(BRect update) override {
  58. (void)update; // silence unused-parameter warning
  59. if (fCanvas && fCanvas->IsValid()) {
  60. DrawBitmap(fCanvas, BPoint(0, 0));
  61. }
  62. }
  63.  
  64. void MessageReceived(BMessage* msg) override {
  65. if (msg->what == kMsgTick) {
  66. // printf("Tick: advancing from frame %d\n", fFrame);
  67. _Advance();
  68. Invalidate();
  69. } else {
  70. BView::MessageReceived(msg);
  71. }
  72. }
  73.  
  74. private:
  75. void _PrepareCanvas() {
  76. delete fCanvas;
  77. BRect rect(0, 0, fGif->SWidth - 1, fGif->SHeight - 1);
  78. fCanvas = new BBitmap(rect, B_RGBA32, true);
  79. if (!fCanvas || !fCanvas->IsValid())
  80. throw std::runtime_error("Failed to allocate canvas bitmap");
  81.  
  82. // start fully transparent (or you can fill with background color if desired)
  83. memset(fCanvas->Bits(), 0, fCanvas->BitsLength());
  84. }
  85.  
  86. void _ClearRectToBG(const GifImageDesc& idesc) {
  87. if (!fCanvas) return;
  88. uint8* bits = (uint8*)fCanvas->Bits();
  89. const int32 bpr = fCanvas->BytesPerRow();
  90.  
  91. for (int y = 0; y < idesc.Height; y++) {
  92. uint8* row = bits + (idesc.Top + y) * bpr + (idesc.Left * 4);
  93. for (int x = 0; x < idesc.Width; x++) {
  94. row[0] = 0; // blue
  95. row[1] = 0; // green
  96. row[2] = 0; // red
  97. row[3] = 0; // alpha
  98. row += 4;
  99. }
  100. }
  101. }
  102.  
  103. void _BlitFrame(const SavedImage& si, const GraphicsControlBlock& gcb) {
  104. ColorMapObject* cmap = si.ImageDesc.ColorMap ? si.ImageDesc.ColorMap : fGif->SColorMap;
  105. if (!cmap) return;
  106.  
  107. uint8* bits = (uint8*)fCanvas->Bits();
  108. const int32 bpr = fCanvas->BytesPerRow();
  109. const GifByteType* src = si.RasterBits;
  110. const int transparent = gcb.TransparentColor; // -1 means none
  111.  
  112. for (int y = 0; y < si.ImageDesc.Height; y++) {
  113. uint8* row = bits + (si.ImageDesc.Top + y) * bpr + (si.ImageDesc.Left * 4);
  114. for (int x = 0; x < si.ImageDesc.Width; x++) {
  115. int idx = *src++;
  116. if (idx == transparent) {
  117. // leave underlying pixel as-is
  118. } else if (idx >= 0 && idx < cmap->ColorCount) {
  119. const GifColorType& col = cmap->Colors[idx];
  120. row[0] = col.Blue;
  121. row[1] = col.Green;
  122. row[2] = col.Red;
  123. row[3] = 255;
  124. }
  125. row += 4;
  126. }
  127. }
  128. }
  129.  
  130. void _RenderFrame(int index) {
  131. if (!fCanvas || index < 0 || index >= fGif->ImageCount) return;
  132.  
  133. // apply disposal of previous frame BEFORE drawing this one
  134. if (fHasLastRect && fLastDisposal == DISPOSE_BACKGROUND) {
  135. _ClearRectToBG(fLastRect);
  136. }
  137.  
  138. // get GCB for this frame (safe default if call fails)
  139. GraphicsControlBlock gcb;
  140. memset(&gcb, 0, sizeof(gcb));
  141. if (DGifSavedExtensionToGCB(fGif, index, &gcb) == GIF_ERROR) {
  142. // leave gcb as zeros (delay = 0 etc.)
  143. }
  144.  
  145. _BlitFrame(fGif->SavedImages[index], gcb);
  146.  
  147. // store for next disposal handling
  148. fLastRect = fGif->SavedImages[index].ImageDesc;
  149. fHasLastRect = true;
  150. fLastDisposal = gcb.DisposalMode;
  151. fLastDelayCS = (gcb.DelayTime > 0 ? gcb.DelayTime : 10); // default 0.1s
  152. }
  153.  
  154. void _Advance() {
  155. fFrame = (fFrame + 1) % fGif->ImageCount;
  156. _RenderFrame(fFrame);
  157. _ScheduleNextTick();
  158. }
  159.  
  160. void _ScheduleNextTick() {
  161. // centiseconds -> microseconds
  162. const bigtime_t usec = (bigtime_t)fLastDelayCS * 10000LL;
  163.  
  164. // recreate runner so SetInterval quirks don't bite older Haiku builds
  165. delete fRunner;
  166. fRunner = nullptr;
  167.  
  168. BMessage tick(kMsgTick);
  169.  
  170. // create runner targeting this view (now valid because we only call this in AttachedToWindow)
  171. BMessenger target(this);
  172.  
  173. // enforce a reasonable minimum interval (0.05s or 0.1s) to avoid very tiny values
  174. const bigtime_t chosen = (usec >= 20000LL) ? usec : 100000LL;
  175.  
  176. fRunner = new BMessageRunner(target, &tick, chosen);
  177.  
  178. // debug
  179. // printf("Scheduled runner: interval = %lld us (frame %d)\n",
  180. // (long long)chosen, fFrame);
  181. }
  182.  
  183. private:
  184. GifFileType* fGif;
  185. BBitmap* fCanvas;
  186. BMessageRunner* fRunner;
  187. int fFrame;
  188.  
  189. GifImageDesc fLastRect;
  190. int fLastDisposal;
  191. bool fHasLastRect;
  192. int fLastDelayCS; // centiseconds
  193. };
  194.  
  195. // --- Window + app -----------------------------------------------------------
  196.  
  197. class GifWindow : public BWindow {
  198. public:
  199. GifWindow(GifFileType* gif)
  200. : BWindow(BRect(100, 100, 100 + gif->SWidth, 100 + gif->SHeight),
  201. "GIF Viewer",
  202. B_TITLED_WINDOW, B_NOT_ZOOMABLE | B_NOT_RESIZABLE)
  203. {
  204. AddChild(new GifView(Bounds(), gif));
  205. Show();
  206. }
  207.  
  208. bool QuitRequested() override {
  209. be_app->PostMessage(B_QUIT_REQUESTED);
  210. return true;
  211. }
  212. };
  213.  
  214. class GifApp : public BApplication {
  215. public:
  216. GifApp(const char* gifPath) : BApplication("application/x-vnd.example-gifview"), fGif(nullptr) {
  217. _OpenGif(gifPath);
  218. }
  219.  
  220. ~GifApp() override {
  221. if (fGif) {
  222. int err = 0;
  223. #if GIFLIB_MAJOR >= 5
  224. DGifCloseFile(fGif, &err);
  225. #else
  226. DGifCloseFile(fGif);
  227. #endif
  228. fGif = nullptr;
  229. }
  230. }
  231.  
  232. void ReadyToRun() override {
  233. new GifWindow(fGif);
  234. }
  235.  
  236. private:
  237. void _OpenGif(const char* path) {
  238. int gifErr = 0;
  239. #if GIFLIB_MAJOR >= 5
  240. fGif = DGifOpenFileName(path, &gifErr);
  241. #else
  242. fGif = DGifOpenFileName(path);
  243. #endif
  244. if (!fGif) {
  245. fprintf(stderr, "Failed to open GIF: %s (err=%d)\n", path, gifErr);
  246. throw std::runtime_error("DGifOpenFileName failed");
  247. }
  248. if (DGifSlurp(fGif) == GIF_ERROR) {
  249. throw std::runtime_error("DGifSlurp failed");
  250. }
  251. if (fGif->ImageCount <= 0) throw std::runtime_error("GIF has no frames");
  252. }
  253.  
  254. GifFileType* fGif;
  255. };
  256.  
  257. // --- main ------------------------------------------------------------------
  258.  
  259. int main(int argc, char** argv)
  260. {
  261. if (argc < 2) {
  262. fprintf(stderr, "Usage: %s /path/to/animation.gif\n", argv[0]);
  263. return 1;
  264. }
  265. try {
  266. GifApp app(argv[1]);
  267. app.Run();
  268. } catch (const std::exception& e) {
  269. fprintf(stderr, "Error: %s\n", e.what());
  270. return 1;
  271. }
  272. return 0;
  273. }
  274.  
Advertisement
Add Comment
Please, Sign In to add comment