// Copyright 2014 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. import 'dart:io'; import 'dart:ui' show Size, Picture, Image; import 'package:flutter/foundation.dart'; /// Whether to replace all shadows with solid color blocks. /// /// This is useful when writing golden file tests (see [matchesGoldenFile]) since /// the rendering of shadows is not guaranteed to be pixel-for-pixel identical from /// version to version (or even from run to run). /// /// In those tests, this is usually set to false at the beginning of a test and back /// to true before the end of the test case. /// /// If it remains true when the test ends, an exception is thrown to avoid state /// leaking from one test case to another. bool debugDisableShadows = false; /// Signature for a method that returns an [HttpClient]. /// /// Used by [debugNetworkImageHttpClientProvider]. typedef HttpClientProvider = HttpClient Function(); /// Provider from which [NetworkImage] will get its [HttpClient] in debug builds. /// /// If this value is unset, [NetworkImage] will use its own internally-managed /// [HttpClient]. /// /// This setting can be overridden for testing to ensure that each test receives /// a mock client that hasn't been affected by other tests. /// /// This value is ignored in non-debug builds. HttpClientProvider? debugNetworkImageHttpClientProvider; /// Called when the framework is about to paint an [Image] to a [Canvas] with an /// [ImageSizeInfo] that contains the decoded size of the image as well as its /// output size. /// /// See: [debugOnPaintImage]. typedef PaintImageCallback = void Function(ImageSizeInfo); /// Tracks the bytes used by a [dart:ui.Image] compared to the bytes needed to /// paint that image without scaling it. @immutable class ImageSizeInfo { /// Creates an object to track the backing size of a [dart:ui.Image] compared /// to its display size on a [Canvas]. /// /// This class is used by the framework when it paints an image to a canvas /// to report to `dart:developer`'s [postEvent], as well as to the /// [debugOnPaintImage] callback if it is set. const ImageSizeInfo({this.source, required this.displaySize, required this.imageSize}); /// A unique identifier for this image, for example its asset path or network /// URL. final String? source; /// The size of the area the image will be rendered in. final Size displaySize; /// The size the image has been decoded to. final Size imageSize; /// The number of bytes needed to render the image without scaling it. int get displaySizeInBytes => _sizeToBytes(displaySize); /// The number of bytes used by the image in memory. int get decodedSizeInBytes => _sizeToBytes(imageSize); int _sizeToBytes(Size size) { // Assume 4 bytes per pixel and that mipmapping will be used, which adds // 4/3. return (size.width * size.height * 4 * (4/3)).toInt(); } /// Returns a JSON encodable representation of this object. Map<String, Object?> toJson() { return <String, Object?>{ 'source': source, 'displaySize': <String, Object?>{ 'width': displaySize.width, 'height': displaySize.height, }, 'imageSize': <String, Object?>{ 'width': imageSize.width, 'height': imageSize.height, }, 'displaySizeInBytes': displaySizeInBytes, 'decodedSizeInBytes': decodedSizeInBytes, }; } @override bool operator ==(Object other) { if (other.runtimeType != runtimeType) { return false; } return other is ImageSizeInfo && other.source == source && other.imageSize == imageSize && other.displaySize == displaySize; } @override int get hashCode => Object.hash(source, displaySize, imageSize); @override String toString() => 'ImageSizeInfo($source, imageSize: $imageSize, displaySize: $displaySize)'; } /// If not null, called when the framework is about to paint an [Image] to a /// [Canvas] with an [ImageSizeInfo] that contains the decoded size of the /// image as well as its output size. /// /// A test can use this callback to detect if images under test are being /// rendered with the appropriate cache dimensions. /// /// For example, if a 100x100 image is decoded it takes roughly 53kb in memory /// (including mipmapping overhead). If it is only ever displayed at 50x50, it /// would take only 13kb if the cacheHeight/cacheWidth parameters had been /// specified at that size. This problem becomes more serious for larger /// images, such as a high resolution image from a 12MP camera, which would be /// 64mb when decoded. /// /// When using this callback, developers should consider whether the image will /// be panned or scaled up in the application, how many images are being /// displayed, and whether the application will run on multiple devices with /// different resolutions and memory capacities. For example, it should be fine /// to have an image that animates from thumbnail size to full screen be at /// a higher resolution while animating, but it would be problematic to have /// a grid or list of such thumbnails all be at the full resolution at the same /// time. PaintImageCallback? debugOnPaintImage; /// If true, the framework will color invert and horizontally flip images that /// have been decoded to a size taking at least [debugImageOverheadAllowance] /// bytes more than necessary. /// /// It will also call [FlutterError.reportError] with information about the /// image's decoded size and its display size, which can be used resize the /// asset before shipping it, apply `cacheHeight` or `cacheWidth` parameters, or /// directly use a [ResizeImage]. Whenever possible, resizing the image asset /// itself should be preferred, to avoid unnecessary network traffic, disk space /// usage, and other memory overhead incurred during decoding. /// /// Developers using this flag should test their application on appropriate /// devices and display sizes for their expected deployment targets when using /// these parameters. For example, an application that responsively resizes /// images for a desktop and mobile layout should avoid decoding all images at /// sizes appropriate for mobile when on desktop. Applications should also avoid /// animating these parameters, as each change will result in a newly decoded /// image. For example, an image that always grows into view should decode only /// at its largest size, whereas an image that normally is a thumbnail and then /// pops into view should be decoded at its smallest size for the thumbnail and /// the largest size when needed. /// /// This has no effect unless asserts are enabled. bool debugInvertOversizedImages = false; const int _imageOverheadAllowanceDefault = 128 * 1024; /// The number of bytes an image must use before it triggers inversion when /// [debugInvertOversizedImages] is true. /// /// Default is 128kb. int debugImageOverheadAllowance = _imageOverheadAllowanceDefault; /// Returns true if none of the painting library debug variables have been changed. /// /// This function is used by the test framework to ensure that debug variables /// haven't been inadvertently changed. /// /// See [the painting library](painting/painting-library.html) for a complete /// list. /// /// The `debugDisableShadowsOverride` argument can be provided to override /// the expected value for [debugDisableShadows]. (This exists because the /// test framework itself overrides this value in some cases.) bool debugAssertAllPaintingVarsUnset(String reason, { bool debugDisableShadowsOverride = false }) { assert(() { if (debugDisableShadows != debugDisableShadowsOverride || debugNetworkImageHttpClientProvider != null || debugOnPaintImage != null || debugInvertOversizedImages == true || debugImageOverheadAllowance != _imageOverheadAllowanceDefault) { throw FlutterError(reason); } return true; }()); return true; } /// The signature of [debugCaptureShaderWarmUpPicture]. /// /// Used by tests to run assertions on the [Picture] created by /// [ShaderWarmUp.execute]. The return value indicates whether the assertions /// pass or not. typedef ShaderWarmUpPictureCallback = bool Function(Picture); /// The signature of [debugCaptureShaderWarmUpImage]. /// /// Used by tests to run assertions on the [Image] created by /// [ShaderWarmUp.execute]. The return value indicates whether the assertions /// pass or not. typedef ShaderWarmUpImageCallback = bool Function(Image); /// Called by [ShaderWarmUp.execute] immediately after it creates a [Picture]. /// /// Tests may use this to capture the picture and run assertions on it. ShaderWarmUpPictureCallback debugCaptureShaderWarmUpPicture = _defaultPictureCapture; bool _defaultPictureCapture(Picture picture) => true; /// Called by [ShaderWarmUp.execute] immediately after it creates an [Image]. /// /// Tests may use this to capture the picture and run assertions on it. ShaderWarmUpImageCallback debugCaptureShaderWarmUpImage = _defaultImageCapture; bool _defaultImageCapture(Image image) => true;