Skip to content

Commit

Permalink
gif_recording: Implement the GIF encoder
Browse files Browse the repository at this point in the history
This CL implements all the remaining parts of the GIF encoder.
The color quantization algorithm used here is simple and will
eventually be replaced later with a faster, better one.

Demo: http://b/268646261#comment2

Fixed: b/268646261
Test: Manually

Change-Id: I98231861c23d177d0128c27e257dcd1a330d0acc
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4266670
Reviewed-by: Brian Osman <brianosman@google.com>
Reviewed-by: Will Harris <wfh@chromium.org>
Reviewed-by: Michele Fan <michelefan@chromium.org>
Reviewed-by: Rick Byers <rbyers@chromium.org>
Commit-Queue: Ahmed Fakhry <afakhry@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1110478}
  • Loading branch information
Ahmed Fakhry authored and ReyBan82 committed Feb 27, 2024
1 parent 21ba459 commit ad88d96
Show file tree
Hide file tree
Showing 21 changed files with 715 additions and 19 deletions.
4 changes: 4 additions & 0 deletions ash/capture_mode/capture_mode_controller.cc
Original file line number Diff line number Diff line change
Expand Up @@ -375,6 +375,10 @@ void EmitServiceRecordingStatus(recording::mojom::RecordingStatus status) {
case RecordingStatus::kLowDriveFsQuota:
RecordEndRecordingReason(EndRecordingReason::kLowDriveFsQuota);
break;
case RecordingStatus::kVideoEncoderReconfigurationFailure:
RecordEndRecordingReason(
EndRecordingReason::kVideoEncoderReconfigurationFailure);
break;
}
}

Expand Down
3 changes: 2 additions & 1 deletion ash/capture_mode/capture_mode_metrics.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,8 @@ enum class EndRecordingReason {
kVideoEncodingError,
kProjectorTranscriptionError,
kLowDriveFsQuota,
kMaxValue = kLowDriveFsQuota,
kVideoEncoderReconfigurationFailure,
kMaxValue = kVideoEncoderReconfigurationFailure,
};

// Enumeration of capture bar buttons that can be pressed while in capture mode.
Expand Down
3 changes: 3 additions & 0 deletions chromeos/ash/services/recording/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,11 @@ assert(is_chromeos_ash)

source_set("recording") {
sources = [
"color_quantization.cc",
"color_quantization.h",
"gif_encoder.cc",
"gif_encoder.h",
"gif_encoding_types.h",
"gif_file_writer.cc",
"gif_file_writer.h",
"lzw_pixel_color_indices_writer.cc",
Expand Down
2 changes: 2 additions & 0 deletions chromeos/ash/services/recording/DEPS
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ include_rules = [
"+services/viz/privileged/mojom/compositing",
"+testing/gmock/include/gmock/gmock.h",
"+testing/gtest/include/gtest/gtest.h",
"+third_party/skia/include/core/SkBitmap.h",
"+third_party/skia/include/core/SkColor.h",
"+ui/gfx",

# Abseil is allowed by default, but some features are banned. See
Expand Down
103 changes: 103 additions & 0 deletions chromeos/ash/services/recording/color_quantization.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chromeos/ash/services/recording/color_quantization.h"

#include <cstdint>

#include "base/check_op.h"
#include "chromeos/ash/services/recording/gif_encoding_types.h"

namespace recording {

namespace {

// If no color exists in the color table whose squared distance to the current
// color being added is smaller than this value, the new color will be added to
// the table.
constexpr uint32_t kMinSquaredDistanceToAddColor = 75;

// Calculates and returns the squared value of the Euclidean distance between
// the two given colors.
uint32_t CalculateColorDistanceSquared(const SkColor& color_a,
const SkColor& color_b) {
const uint32_t diff_r = SkColorGetR(color_a) - SkColorGetR(color_b);
const uint32_t diff_g = SkColorGetG(color_a) - SkColorGetG(color_b);
const uint32_t diff_b = SkColorGetB(color_a) - SkColorGetB(color_b);
return diff_r * diff_r + diff_g * diff_g + diff_b * diff_b;
}

// If `new_color` already exists in `out_palette`, it returns its index
// immediately. Otherwise, it tries to add it to the palette if possible (i.e.
// if there's still room in the palette and there's no other color that is
// considered close enough), and returns the index. If addition is not possible,
// it returns the index of the closest color in the palette.
ColorIndex MaybeAddColorToPalette(const SkColor& new_color,
ColorTable& out_palette) {
int index_of_closest = -1;
uint32_t min_squared_distance = std::numeric_limits<uint32_t>::max();
const size_t current_size = out_palette.size();
for (size_t i = 0; i < current_size; ++i) {
const auto& current_color = out_palette[i];
if (current_color == new_color) {
return i;
}

const uint32_t squared_distance =
CalculateColorDistanceSquared(new_color, current_color);
if (squared_distance < min_squared_distance) {
min_squared_distance = squared_distance;
index_of_closest = i;
}
}

if (current_size < kMaxNumberOfColorsInPalette &&
min_squared_distance >= kMinSquaredDistanceToAddColor) {
out_palette.push_back(new_color);
return current_size;
}

DCHECK_NE(index_of_closest, -1);
return index_of_closest;
}

} // namespace

// TODO(b/270604745): Implement a better color quantization algorithm.
void BuildColorPaletteAndPixelIndices(const SkBitmap& bitmap,
ColorTable& out_color_palette,
ColorIndices& out_pixel_color_indices) {
out_color_palette.clear();
out_pixel_color_indices.clear();

for (int row = 0; row < bitmap.height(); ++row) {
for (int col = 0; col < bitmap.width(); ++col) {
// We do not care about the alpha values, since our palette contains only
// RGB colors, therefore we make the color as fully opaque before calling
// `MaybeAddColorToPalette()` to make color comparison with the `==`
// operator possible.
const auto color = SkColorSetA(bitmap.getColor(col, row), 0xFF);
const ColorIndex index = MaybeAddColorToPalette(color, out_color_palette);
DCHECK_GE(index, 0);
DCHECK_LT(index, out_color_palette.size());
out_pixel_color_indices.push_back(index);
}
}

DCHECK_LE(out_color_palette.size(), kMaxNumberOfColorsInPalette);
}

uint8_t CalculateColorBitDepth(const ColorTable& color_palette) {
DCHECK_LE(color_palette.size(), kMaxNumberOfColorsInPalette);

uint8_t bit_depth = 1;
while ((1u << bit_depth) < color_palette.size()) {
++bit_depth;
}

DCHECK_LE(bit_depth, kMaxColorBitDepth);
return bit_depth;
}

} // namespace recording
34 changes: 34 additions & 0 deletions chromeos/ash/services/recording/color_quantization.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef CHROMEOS_ASH_SERVICES_RECORDING_COLOR_QUANTIZATION_H_
#define CHROMEOS_ASH_SERVICES_RECORDING_COLOR_QUANTIZATION_H_

#include "chromeos/ash/services/recording/gif_encoding_types.h"
#include "third_party/skia/include/core/SkBitmap.h"

namespace recording {

// GIF images can have a maximum number of 256 colors in their color tables.
// This means that the minimum number of bits needed to represent this count is
// 8, which is the max bit depth value.
constexpr size_t kMaxNumberOfColorsInPalette = 256;
constexpr uint8_t kMaxColorBitDepth = 8;

// Performs color quantization on the given `bitmap` and fills
// `out_color_palette` with the most important 256 colors in the image, and also
// fills `out_pixel_color_indices` with the indices of the chosen colors from
// `out_color_palette` for all the pixels in `bitmap`.
void BuildColorPaletteAndPixelIndices(const SkBitmap& bitmap,
ColorTable& out_color_palette,
ColorIndices& out_pixel_color_indices);

// Calculates and returns the color bit depth based on the size of the given
// `color_palette`. The color bit depth is the least number of bits needed to be
// able to represent the size of the palette as a binary number.
uint8_t CalculateColorBitDepth(const ColorTable& color_palette);

} // namespace recording

#endif // CHROMEOS_ASH_SERVICES_RECORDING_COLOR_QUANTIZATION_H_
Loading

0 comments on commit ad88d96

Please sign in to comment.