Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
convert-to-nia.cpp
Go to the documentation of this file.
1/*
2 * Copyright 2020 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8// This program converts an image from stdin (e.g. a JPEG, PNG, etc.) to stdout
9// (in the NIA/NIE format, a trivial image file format).
10//
11// The NIA/NIE file format specification is at:
12// https://github.com/google/wuffs/blob/master/doc/spec/nie-spec.md
13//
14// Pass "-1" or "-first-frame-only" as a command line flag to output NIE (a
15// still image) instead of NIA (an animated image). The output format (NIA or
16// NIE) depends only on this flag's absence or presence, not on the stdin
17// image's format.
18//
19// There are multiple codec implementations of any given image format. For
20// example, as of May 2020, Chromium, Skia and Wuffs each have their own BMP
21// decoder implementation. There is no standard "libbmp" that they all share.
22// Comparing this program's output (or hashed output) to similar programs in
23// other repositories can identify image inputs for which these decoders (or
24// different versions of the same decoder) produce different output (pixels).
25//
26// An equivalent program (using the Chromium image codecs) is at:
27// https://crrev.com/c/2210331
28//
29// An equivalent program (using the Wuffs image codecs) is at:
30// https://github.com/google/wuffs/blob/master/example/convert-to-nia/convert-to-nia.c
31
32#include <stdio.h>
33#include <string.h>
34
37#include "include/core/SkData.h"
39
40static inline void set_u32le(uint8_t* ptr, uint32_t val) {
41 ptr[0] = val >> 0;
42 ptr[1] = val >> 8;
43 ptr[2] = val >> 16;
44 ptr[3] = val >> 24;
45}
46
47static inline void set_u64le(uint8_t* ptr, uint64_t val) {
48 ptr[0] = val >> 0;
49 ptr[1] = val >> 8;
50 ptr[2] = val >> 16;
51 ptr[3] = val >> 24;
52 ptr[4] = val >> 32;
53 ptr[5] = val >> 40;
54 ptr[6] = val >> 48;
55 ptr[7] = val >> 56;
56}
57
58static void write_nix_header(uint32_t magicU32le, uint32_t width, uint32_t height) {
59 uint8_t data[16];
60 set_u32le(data + 0, magicU32le);
61 set_u32le(data + 4, 0x346E62FF); // 4 bytes per pixel non-premul BGRA.
62 set_u32le(data + 8, width);
63 set_u32le(data + 12, height);
64 fwrite(data, 1, 16, stdout);
65}
66
67static bool write_nia_duration(uint64_t totalDurationMillis) {
68 // Flicks are NIA's unit of time. One flick (frame-tick) is 1 / 705_600_000
69 // of a second. See https://github.com/OculusVR/Flicks
70 static constexpr uint64_t flicksPerMilli = 705600;
71 if (totalDurationMillis > (INT64_MAX / flicksPerMilli)) {
72 // Converting from millis to flicks would overflow.
73 return false;
74 }
75
76 uint8_t data[8];
77 set_u64le(data + 0, totalDurationMillis * flicksPerMilli);
78 fwrite(data, 1, 8, stdout);
79 return true;
80}
81
82static void write_nie_pixels(uint32_t width, uint32_t height, const SkBitmap& bm) {
83 static constexpr size_t kBufferSize = 4096;
84 uint8_t buf[kBufferSize];
85 size_t n = 0;
86 for (uint32_t y = 0; y < height; y++) {
87 for (uint32_t x = 0; x < width; x++) {
88 SkColor c = bm.getColor(x, y);
89 buf[n++] = SkColorGetB(c);
90 buf[n++] = SkColorGetG(c);
91 buf[n++] = SkColorGetR(c);
92 buf[n++] = SkColorGetA(c);
93 if (n == kBufferSize) {
94 fwrite(buf, 1, n, stdout);
95 n = 0;
96 }
97 }
98 }
99 if (n > 0) {
100 fwrite(buf, 1, n, stdout);
101 }
102}
103
104static void write_nia_padding(uint32_t width, uint32_t height) {
105 // 4 bytes of padding when the width and height are both odd.
106 if (width & height & 1) {
107 uint8_t data[4];
108 set_u32le(data + 0, 0);
109 fwrite(data, 1, 4, stdout);
110 }
111}
112
113static void write_nia_footer(int repetitionCount, bool stillImage) {
114 uint8_t data[8];
115 if (stillImage || (repetitionCount == SkCodec::kRepetitionCountInfinite)) {
116 set_u32le(data + 0, 0);
117 } else {
118 // NIA's loop count and Skia's repetition count differ by one. See
119 // https://github.com/google/wuffs/blob/master/doc/spec/nie-spec.md#nii-footer
120 set_u32le(data + 0, 1 + repetitionCount);
121 }
122 set_u32le(data + 4, 0x80000000);
123 fwrite(data, 1, 8, stdout);
124}
125
126int main(int argc, char** argv) {
127 bool firstFrameOnly = false;
128 for (int a = 1; a < argc; a++) {
129 if ((strcmp(argv[a], "-1") == 0) || (strcmp(argv[a], "-first-frame-only") == 0)) {
130 firstFrameOnly = true;
131 break;
132 }
133 }
134
135 std::unique_ptr<SkCodec> codec(SkCodec::MakeFromData(SkData::MakeFromFILE(stdin)));
136 if (!codec) {
137 SkDebugf("Decode failed.\n");
138 return 1;
139 }
140 codec->getInfo().makeColorSpace(nullptr);
141 SkBitmap bm;
142 bm.allocPixels(codec->getInfo());
143 size_t bmByteSize = bm.computeByteSize();
144
145 // Cache a frame that future frames may depend on.
146 int cachedFrame = SkCodec::kNoFrame;
147 SkAutoMalloc cachedFramePixels;
148
149 uint64_t totalDurationMillis = 0;
150 const int frameCount = codec->getFrameCount();
151 if (frameCount == 0) {
152 SkDebugf("No frames.\n");
153 return 1;
154 }
155 std::vector<SkCodec::FrameInfo> frameInfos = codec->getFrameInfo();
156 bool stillImage = frameInfos.size() <= 1;
157
158 for (int i = 0; i < frameCount; i++) {
159 SkCodec::Options opts;
160 opts.fFrameIndex = i;
161
162 if (!stillImage) {
163 int durationMillis = frameInfos[i].fDuration;
164 if (durationMillis < 0) {
165 SkDebugf("Negative animation duration.\n");
166 return 1;
167 }
168 totalDurationMillis += static_cast<uint64_t>(durationMillis);
169 if (totalDurationMillis > INT64_MAX) {
170 SkDebugf("Unsupported animation duration.\n");
171 return 1;
172 }
173
174 if ((cachedFrame != SkCodec::kNoFrame) &&
175 (cachedFrame == frameInfos[i].fRequiredFrame) && cachedFramePixels.get()) {
176 opts.fPriorFrame = cachedFrame;
177 memcpy(bm.getPixels(), cachedFramePixels.get(), bmByteSize);
178 }
179 }
180
181 if (!firstFrameOnly) {
182 if (i == 0) {
183 write_nix_header(0x41AFC36E, // "nïA" magic string as a u32le.
184 bm.width(), bm.height());
185 }
186
187 if (!write_nia_duration(totalDurationMillis)) {
188 SkDebugf("Unsupported animation duration.\n");
189 return 1;
190 }
191 }
192
193 const SkCodec::Result result =
194 codec->getPixels(codec->getInfo(), bm.getPixels(), bm.rowBytes(), &opts);
196 SkDebugf("Decode frame pixels #%d failed.\n", i);
197 return 1;
198 }
199
200 // If the next frame depends on this one, store it in cachedFrame. It
201 // is possible that we may discard a frame that future frames depend
202 // on, but the codec will simply redecode the discarded frame.
203 if ((static_cast<size_t>(i + 1) < frameInfos.size()) &&
204 (frameInfos[i + 1].fRequiredFrame == i)) {
205 cachedFrame = i;
206 memcpy(cachedFramePixels.reset(bmByteSize), bm.getPixels(), bmByteSize);
207 }
208
209 int width = bm.width();
210 int height = bm.height();
211 write_nix_header(0x45AFC36E, // "nïE" magic string as a u32le.
212 width, height);
215 SkDebugf("Incomplete input.\n");
216 return 1;
217 }
218 if (firstFrameOnly) {
219 return 0;
220 }
222 }
223 write_nia_footer(codec->getRepetitionCount(), stillImage);
224 return 0;
225}
#define SkColorGetR(color)
Definition SkColor.h:65
#define SkColorGetG(color)
Definition SkColor.h:69
uint32_t SkColor
Definition SkColor.h:37
#define SkColorGetA(color)
Definition SkColor.h:61
#define SkColorGetB(color)
Definition SkColor.h:73
void SK_SPI SkDebugf(const char format[],...) SK_PRINTF_LIKE(1
static const size_t kBufferSize
Definition SkString.cpp:27
void * reset(size_t size=0, OnShrink shrink=kAlloc_OnShrink)
void * get()
void allocPixels(const SkImageInfo &info, size_t rowBytes)
Definition SkBitmap.cpp:258
SkColor getColor(int x, int y) const
Definition SkBitmap.h:874
size_t computeByteSize() const
Definition SkBitmap.h:293
int width() const
Definition SkBitmap.h:149
size_t rowBytes() const
Definition SkBitmap.h:238
void * getPixels() const
Definition SkBitmap.h:283
int height() const
Definition SkBitmap.h:158
static std::unique_ptr< SkCodec > MakeFromData(sk_sp< SkData >, SkSpan< const SkCodecs::Decoder > decoders, SkPngChunkReader *=nullptr)
Definition SkCodec.cpp:241
@ kIncompleteInput
Definition SkCodec.h:84
@ kSuccess
Definition SkCodec.h:80
static constexpr int kRepetitionCountInfinite
Definition SkCodec.h:759
static constexpr int kNoFrame
Definition SkCodec.h:650
static sk_sp< SkData > MakeFromFILE(FILE *f)
Definition SkData.cpp:138
static void write_nie_pixels(uint32_t width, uint32_t height, const SkBitmap &bm)
static void write_nia_padding(uint32_t width, uint32_t height)
static void set_u64le(uint8_t *ptr, uint64_t val)
static bool write_nia_duration(uint64_t totalDurationMillis)
static void set_u32le(uint8_t *ptr, uint32_t val)
static void write_nix_header(uint32_t magicU32le, uint32_t width, uint32_t height)
static void write_nia_footer(int repetitionCount, bool stillImage)
struct MyStruct a[10]
GAsyncResult * result
char ** argv
Definition library.h:9
double y
double x
Definition main.py:1
int32_t height
int32_t width