# How the Telegram Desktop Media Preview Widget Handles Various File Types

> Discover how the Telegram Desktop media preview widget efficiently renders diverse file types like photos, stickers, Lottie, WebM, and GIFs using specialized pipelines for scaled cached previews.

- Repository: [Telegram Desktop/tdesktop](https://github.com/telegramdesktop/tdesktop)
- Tags: internals
- Published: 2026-04-05

---

**The `Window::MediaPreviewWidget` in telegramdesktop/tdesktop detects whether the supplied media is a photo, static sticker, Lottie animation, WebM video, or GIF, then routes each type through specialized rendering pipelines—including `PhotoMedia` views, `DocumentMedia` stickers, `Lottie::SinglePlayer` for JSON animations, and `Media::Clip::Reader` for WebM/GIF clips—to produce a scaled, cached preview.**

The media preview widget in Telegram Desktop provides a lightweight, transient overlay that appears when users hover over messages or drag files. As implemented in the `telegramdesktop/tdesktop` repository, this component must gracefully handle everything from static images to complex animated stickers. Understanding how the media preview widget handles various file types requires examining the type-detection logic in [`Telegram/SourceFiles/window/window_media_preview.cpp`](https://github.com/telegramdesktop/tdesktop/blob/main/Telegram/SourceFiles/window/window_media_preview.cpp) and the specialized media views that power each rendering path.

## Type Dispatch and Core Logic

The widget determines what to display by inspecting the **type of the supplied pointer** passed to its `showPreview()` entry points. There are two primary overloads: one accepting `PhotoData*` and another accepting `DocumentData*`. According to the source code at [lines 24‑35](https://github.com/telegramdesktop/tdesktop/blob/dev/Telegram/SourceFiles/window/window_media_preview.cpp#L24-L35), the implementation stores the file origin, clears any previously held media pointer, creates a media view, and requests thumbnails.

Once the preview is activated, `currentImage()` serves as the dispatch hub for rendering. This method branches based on runtime type checks—distinguishing between photos, static stickers, Lottie animations, and WebM/GIF content—to determine which cache or player to initialize.

### Photos

When `showPreview(origin, photo)` is invoked, the widget creates a `PhotoMedia` view via `photo->createMediaView()` ([lines 24‑35](https://github.com/telegramdesktop/tdesktop/blob/dev/Telegram/SourceFiles/window/window_media_preview.cpp#L24-L35)). The view attempts to load the full-size image; if unavailable, it falls back to a thumbnail or blurred placeholder. The resulting image is stored in the internal `_cache` member and reused while the preview remains visible ([lines 14‑21](https://github.com/telegramdesktop/tdesktop/blob/dev/Telegram/SourceFiles/window/window_media_preview.cpp#L14-L21)).

### Static Stickers

Documents representing static stickers are identified through `document->sticker()`. If the sticker is **not** a Lottie or WebM variant, the widget queries `_documentMedia->getStickerLarge()` to retrieve the high-resolution image ([lines 59‑70](https://github.com/telegramdesktop/tdesktop/blob/dev/Telegram/SourceFiles/window/window_media_preview.cpp#L59-L70)). This pixmap is then scaled to the dimensions returned by `currentDimensions()` and cached for painting.

### Lottie Animations

Animated stickers using the Lottie (JSON) format are detected via `sticker->isLottie()`. The method `createLottieIfReady()` verifies that the document is fully loaded before invoking `setupLottie()` ([lines 99‑106](https://github.com/telegramdesktop/tdesktop/blob/dev/Telegram/SourceFiles/window/window_media_preview.cpp#L99-L106)). The latter instantiates a `Lottie::SinglePlayer` ([lines 14‑28](https://github.com/telegramdesktop/tdesktop/blob/dev/Telegram/SourceFiles/window/window_media_preview.cpp#L14-L28)) to render frames on-the-fly. An optional premium effect player may be attached to this pipeline for enhanced visual output.

### WebM and GIF Videos

For WebM stickers (detected by `sticker->isWebm()`) and GIF/MP4 animations (`isAnimation()`), the widget uses the **GIF-style** rendering path via `Media::Clip::Reader`. The `validateGifAnimation()` method ([lines 51‑73](https://github.com/telegramdesktop/tdesktop/blob/dev/Telegram/SourceFiles/window/window_media_preview.cpp#L51-L73)) creates either a full-size reader (`_gif`) or a thumbnail reader (`_gifThumbnail`). Frames are decoded on demand through `gif->current()` and drawn as `QPixmap` objects. The widget respects the global pause reason `Window::GifPauseReason::MediaPreview` to manage resource usage.

### Premium Sticker Effects

Premium stickers receive special handling through size modifiers defined as `kPremiumMultiplier`, `kPremiumShift`, and `kPremiumDownscale` ([lines 66‑73](https://github.com/telegramdesktop/tdesktop/blob/dev/Telegram/SourceFiles/window/window_media_preview.cpp#L66-L73)). The content is downscaled before being passed to the Lottie player, then drawn at double size with an effect overlay applied during the paint cycle ([lines 17‑32](https://github.com/telegramdesktop/tdesktop/blob/dev/Telegram/SourceFiles/window/window_media_preview.cpp#L17-L32)).

## Animation Lifecycle and Rendering

The rendering pipeline follows a strict lifecycle to ensure smooth animation and memory efficiency.

**Sizing**: `currentDimensions()` computes the target size based on original media dimensions, custom padding, UI scaling, and the premium downscale factor ([lines 40‑88](https://github.com/telegramdesktop/tdesktop/blob/dev/Telegram/SourceFiles/window/window_media_preview.cpp#L40-L88)).

**Frame Generation**: In `currentImage()` ([lines 56‑124](https://github.com/telegramdesktop/tdesktop/blob/dev/Telegram/SourceFiles/window/window_media_preview.cpp#L56-L124)), the widget selects between:
- Static sticker images from `_documentMedia`
- Photo images from `_photoMedia`
- Lottie frames from `_lottie`
- GIF/WebM frames from the clip reader

**Painting**: The `paintEvent()` method ([lines 79‑66](https://github.com/telegramdesktop/tdesktop/blob/dev/Telegram/SourceFiles/window/window_media_preview.cpp#L79-L66)) draws the background using `st::stickerPreviewBg`, then renders the content. For Lottie, it displays `_lottie->frameInfo`; for GIFs, it uses the current frame from the clip reader. Emoji associated with stickers are rendered after the media layer completes.

## Working with the MediaPreviewWidget API

The following examples demonstrate how to instantiate and configure the widget for different media types.

### Displaying a Document or Sticker

```cpp
// controller: pointer to the current SessionController
// doc: non-null DocumentData* representing the hovered item

auto *preview = new Window::MediaPreviewWidget(parentWidget, controller);
preview->setCustomPadding(QMargins(5, 5, 5, 5));
preview->showPreview(Data::FileOrigin::FromUser, doc);
preview->show();

```

*Key implementation references*: `showPreview(origin, doc)` at [lines 24‑35](https://github.com/telegramdesktop/tdesktop/blob/dev/Telegram/SourceFiles/window/window_media_preview.cpp#L24-L35); `setCustomPadding` at [lines 2‑4](https://github.com/telegramdesktop/tdesktop/blob/dev/Telegram/SourceFiles/window/window_media_preview.cpp#L2-L4).

### Displaying a Photo

```cpp
auto *preview = new Window::MediaPreviewWidget(parentWidget, controller);
preview->setContentShift(10);  // shift preview down by 10 pixels
preview->showPreview(Data::FileOrigin::FromUser, photo);
preview->show();

```

*Key implementation reference*: `showPreview(origin, photo)` at [lines 24‑33](https://github.com/telegramdesktop/tdesktop/blob/dev/Telegram/SourceFiles/window/window_media_preview.cpp#L24-L33).

### Customizing Animation and Appearance

```cpp
auto *preview = new Window::MediaPreviewWidget(parentWidget, controller);
preview->setCustomDuration(crl::time(200));  // 200 ms fade duration
preview->setCustomRadius(8);                 // 8 px corner radius
preview->showPreview(Data::FileOrigin::FromUser, doc);
preview->show();

```

*Key implementation references*: `setCustomDuration` at [lines 18‑20](https://github.com/telegramdesktop/tdesktop/blob/dev/Telegram/SourceFiles/window/window_media_preview.cpp#L18-L20); `setCustomRadius` at [lines 13‑15](https://github.com/telegramdesktop/tdesktop/blob/dev/Telegram/SourceFiles/window/window_media_preview.cpp#L13-L15).

## Key Source Files

| File | Role |
|------|------|
| [[`window/window_media_preview.h`](https://github.com/telegramdesktop/tdesktop/blob/main/window/window_media_preview.h)](https://github.com/telegramdesktop/tdesktop/blob/dev/Telegram/SourceFiles/window/window_media_preview.h) | Public interface of `MediaPreviewWidget` |
| [[`window/window_media_preview.cpp`](https://github.com/telegramdesktop/tdesktop/blob/main/window/window_media_preview.cpp)](https://github.com/telegramdesktop/tdesktop/blob/dev/Telegram/SourceFiles/window/window_media_preview.cpp) | Full implementation: preview lifecycle, sizing, media loading, Lottie/GIF handling, painting |
| [`data/data_document_media.h / .cpp`](https://github.com/telegramdesktop/tdesktop/blob/dev/Telegram/SourceFiles/data/data_document_media.h) | Provides the `DocumentMedia` view used for thumbnails, stickers, and animation bytes |
| [`data/data_photo_media.h / .cpp`](https://github.com/telegramdesktop/tdesktop/blob/dev/Telegram/SourceFiles/data/data_photo_media.h) | Handles loading of photo images at various resolutions |
| [`lottie/lottie_single_player.h / .cpp`](https://github.com/telegramdesktop/tdesktop/blob/dev/Telegram/SourceFiles/lottie/lottie_single_player.h) | Renders Lottie (JSON-based) animations for stickers |
| [`media/clip/media_clip_reader.h / .cpp`](https://github.com/telegramdesktop/tdesktop/blob/dev/Telegram/SourceFiles/media/clip/media_clip_reader.h) | Decodes GIF/WebM animation frames for the preview widget |
| [[`ui/painter.h`](https://github.com/telegramdesktop/tdesktop/blob/main/ui/painter.h)](https://github.com/telegramdesktop/tdesktop/blob/dev/Telegram/SourceFiles/ui/painter.h) | Used in `paintEvent` to draw backgrounds, images, and emojis |

## Summary

- The `MediaPreviewWidget` distinguishes between **photos** and **documents** through overloaded `showPreview()` methods, then routes content through type-specific rendering branches.
- **Static stickers** use `getStickerLarge()` from `DocumentMedia`, while **Lottie animations** require a `SinglePlayer` instance created via `setupLottie()`.
- **WebM and GIF** content share a unified pipeline using `Media::Clip::Reader` managed by `validateGifAnimation()`, with automatic thumbnail fallback.
- **Premium stickers** apply scaling modifiers (`kPremiumDownscale`) and optional effect overlays during the paint cycle.
- All visual content is cached in `_cache` or live-rendered through animation players, with dimensions computed by `currentDimensions()` to respect UI scaling and padding settings.

## Frequently Asked Questions

### How does the widget choose between photo and document rendering?

The widget exposes two `showPreview()` overloads: one accepting `PhotoData*` and another accepting `DocumentData*`. When called, the method stores the appropriate media pointer and clears the opposite type, then creates a corresponding media view (`PhotoMedia` or `DocumentMedia`) to handle subsequent rendering decisions at [lines 24‑35](https://github.com/telegramdesktop/tdesktop/blob/dev/Telegram/SourceFiles/window/window_media_preview.cpp#L24-L35).

### What is the difference between Lottie and WebM sticker handling in the preview?

Lottie stickers trigger the creation of a `Lottie::SinglePlayer` through `setupLottie()` to render JSON animation frames procedurally. WebM stickers, conversely, are treated as video content and routed through `validateGifAnimation()` to create a `Media::Clip::Reader` that decodes compressed video frames, similar to GIF handling ([lines 51‑73](https://github.com/telegramdesktop/tdesktop/blob/dev/Telegram/SourceFiles/window/window_media_preview.cpp#L51-L73)).

### How does the widget handle unloaded or unsupported media types?

When media cannot be loaded—for example, if the document is incomplete or the type is unrecognized—the `_cache` member holds a blank pixmap. The `currentImage()` method returns this empty cache, causing `paintEvent()` to render only the background without content ([lines 42‑44](https://github.com/telegramdesktop/tdesktop/blob/dev/Telegram/SourceFiles/window/window_media_preview.cpp#L42-L44)).

### How are premium sticker effects implemented differently from standard stickers?

Premium stickers apply geometric transformations using `kPremiumMultiplier` and `kPremiumShift` constants. The widget first downscales the content (`kPremiumDownscale`) before passing it to the Lottie renderer, then draws the result at double size with an additional effect layer overlay, as implemented in the size calculation logic at [lines 66‑73](https://github.com/telegramdesktop/tdesktop/blob/dev/Telegram/SourceFiles/window/window_media_preview.cpp#L66-L73).