Skip to content

DPI / PPI Calculator — Kotlin source

Compute screen pixel density (PPI/DPI), dot pitch, physical dimensions, and aspect ratio from any resolution. Solve for PPI from a diagonal, or derive the diagonal from a known PPI - all in your browser.

This is the Kotlin implementation — the same logic the interactive tool runs, in a shareable, citable form.

// =============================================================================
//  DpiPpi.kt — CosmoDev polyglot showcase port of the `dpi-ppi` tool
//  -----------------------------------------------------------------------------
//  Language : Kotlin (1.9, standard library only)
//  Source:   ported from src/lib/dpi-ppi.ts (the canonical, live TypeScript
//             lib); mirrors src/tool-sources/dpi-ppi/{python.py,rust.rs}
//  License  : display source — part of CosmoDev's polyglot tool pages
//             (dev.cosmolabs.org). Shown verbatim alongside the JS/TS/Go/Rust/
//             Python ports and the other language ports.
//  -----------------------------------------------------------------------------
//  Display pixel-density math. Diagonal length in pixels, pixels-per-inch
//  computed from a known physical diagonal, physical width/height/diagonal in
//  both inches and millimeters, dot pitch in millimeters per pixel, and a
//  reduced aspect ratio — all derived from a pixel resolution.
//
//  Pure, deterministic, side-effect free. A zero divisor does not throw:
//  Double division follows IEEE-754, so x / 0.0 yields ±Infinity and
//  0.0 / 0.0 yields NaN — callers should guard with isFinite() before
//  display. (The Python port routes every division through a safeDiv wrapper
//  only because Python raises ZeroDivisionError for float division.)
// =============================================================================

package com.cosmolabs.cosmodev

import kotlin.math.abs
import kotlin.math.sqrt

/** Display pixel-density math. */
object DpiPpi {
    /** Fixed conversion factor between inches and millimeters. */
    private const val MM_PER_INCH = 25.4

    /** Pixels-per-inch result. */
    data class Ppi(val ppi: Double)

    /** Physical dimensions of a display, in inches and millimeters. */
    data class PhysicalSize(
        val widthIn: Double,
        val heightIn: Double,
        val diagonalIn: Double,
        val widthMm: Double,
        val heightMm: Double,
    )

    /**
     * Diagonal length in pixels: √(w² + h²).
     * The sign of the inputs is irrelevant because both terms are squared.
     */
    fun diagonalPixels(w: Double, h: Double): Double = sqrt(w * w + h * h)

    /** Pixels per inch, given a resolution and a known physical diagonal in inches. */
    fun computePpi(w: Double, h: Double, diagonalInches: Double): Ppi =
        Ppi(diagonalPixels(w, h) / diagonalInches)

    /**
     * Derive inches and millimeters from a resolution and a ppi. The diagonal
     * is recomputed from the derived inch dimensions so it stays consistent
     * with the width/height values.
     */
    fun physicalSize(w: Double, h: Double, ppi: Double): PhysicalSize {
        val widthIn = w / ppi
        val heightIn = h / ppi
        return PhysicalSize(
            widthIn = widthIn,
            heightIn = heightIn,
            diagonalIn = diagonalPixels(widthIn, heightIn),
            widthMm = widthIn * MM_PER_INCH,
            heightMm = heightIn * MM_PER_INCH,
        )
    }

    /** Dot pitch (pixel size) in millimeters per pixel: 25.4 / ppi. */
    fun dotPitch(ppi: Double): Double = MM_PER_INCH / ppi

    /**
     * Integer greatest-common-divisor via Euclid's algorithm, used to reduce
     * aspect ratios. Inputs are truncated toward zero (Double.toLong()) and
     * taken in absolute value first, so the sign and fractional part of the
     * inputs never matter.
     */
    private fun gcd(a: Double, b: Double): Long {
        var x = abs(a.toLong())
        var y = abs(b.toLong())
        while (y != 0L) {
            val t = y
            y = x % y
            x = t
        }
        return x
    }

    /**
     * Reduced aspect ratio as "w:h" (e.g. "16:9"). Returns "-" when either
     * side is not a positive number (this also covers NaN, since every
     * comparison with NaN is false).
     */
    fun aspectRatio(w: Double, h: Double): String {
        if (!(w > 0) || !(h > 0)) return "-"
        val g = gcd(w, h)
        if (g == 0L) return "-"
        return "${w.toLong() / g}:${h.toLong() / g}"
    }
}

Also available in 13 other languages

Every CosmoDev tool ships its pure logic in TypeScript (web) and Go (CLI), with authored implementations in a dozen-plus languages — the same contract, ported. Compare all languages side by side →