1 // Copyright (c) 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
7 #include "base/at_exit.h"
8 #include "base/command_line.h"
9 #include "base/files/file_enumerator.h"
10 #include "base/files/file_util.h"
11 #include "base/strings/string_number_conversions.h"
12 #include "base/time/time.h"
13 #include "gpu/skia_runner/in_process_graphics_system.h"
14 #include "gpu/skia_runner/sk_picture_rasterizer.h"
15 #include "third_party/WebKit/public/platform/WebData.h"
16 #include "third_party/WebKit/public/platform/WebImage.h"
17 #include "third_party/WebKit/public/platform/WebSize.h"
18 #include "third_party/skia/include/core/SkOSFile.h"
19 #include "third_party/skia/include/core/SkPicture.h"
20 #include "third_party/skia/include/core/SkStream.h"
21 #include "ui/gfx/codec/png_codec.h"
23 #if defined(OS_MACOSX)
24 #include "base/mac/scoped_nsautorelease_pool.h"
29 bool WriteSkImagePNG(const SkImage
* image
, const base::FilePath
& path
) {
30 DCHECK(!path
.empty());
33 std::cout
<< "Unable to write empty bitmap for " << path
.value() << ".\n";
37 std::string file_path
= path
.MaybeAsASCII();
38 SkFILEWStream
stream(file_path
.c_str());
39 if (!stream
.isValid()) {
40 std::cout
<< "Unable to write to " << file_path
.c_str() << ".\n";
44 SkImageInfo info
= SkImageInfo::Make(image
->width(), image
->height(),
45 SkColorType::kBGRA_8888_SkColorType
,
46 SkAlphaType::kPremul_SkAlphaType
);
47 SkImageInfo::MakeN32Premul(image
->width(), image
->height());
49 const size_t rowBytes
= image
->width() * sizeof(SkPMColor
);
50 std::vector
<SkPMColor
> pixels(image
->width() * image
->height());
52 if (image
->readPixels(info
, pixels
.data(), rowBytes
, 0, 0)) {
53 std::vector
<unsigned char> png_data
;
55 if (gfx::PNGCodec::Encode(
56 reinterpret_cast<const unsigned char*>(pixels
.data()),
57 gfx::PNGCodec::FORMAT_BGRA
,
58 gfx::Size(image
->width(), image
->height()),
59 static_cast<int>(rowBytes
), false,
60 std::vector
<gfx::PNGCodec::Comment
>(), &png_data
)) {
61 if (stream
.write(png_data
.data(), png_data
.size())) {
70 bool onDecode(const void* buffer
, size_t size
, SkBitmap
* bm
) {
71 blink::WebData
web_data(static_cast<const char*>(buffer
), size
);
72 blink::WebImage image
= blink::WebImage::fromData(web_data
, blink::WebSize());
73 if (!image
.isNull()) {
74 *bm
= image
.getSkBitmap();
77 std::cout
<< "Error decoding image.\n";
81 skia::RefPtr
<SkPicture
> ReadPicture(const base::FilePath
& path
) {
82 skia::RefPtr
<SkPicture
> picture
;
83 std::string file_path
= path
.MaybeAsASCII();
84 SkAutoTDelete
<SkStream
> stream(SkStream::NewFromFile(file_path
.c_str()));
86 std::cout
<< "Unable to read " << file_path
.c_str() << ".\n";
90 picture
= skia::AdoptRef(SkPicture::CreateFromStream(stream
.get(), onDecode
));
92 std::cout
<< "Unable to load " << file_path
.c_str()
93 << " as an SkPicture.\n";
98 base::FilePath
MakeDestinationPNGFilename(base::FilePath source_file
,
99 base::FilePath destination_folder
) {
100 base::FilePath filename
= source_file
.BaseName().RemoveExtension();
101 filename
= filename
.AddExtension(FILE_PATH_LITERAL("png"));
102 return destination_folder
.AsEndingWithSeparator().Append(filename
);
105 std::vector
<std::pair
<base::FilePath
, base::FilePath
>> GetSkpsToRasterize(
106 base::FilePath input_path
,
107 base::FilePath output_path
) {
108 std::vector
<std::pair
<base::FilePath
, base::FilePath
>> files
;
110 if (base::DirectoryExists(input_path
)) {
111 if (!output_path
.empty() && !base::DirectoryExists(output_path
))
114 base::FilePath::StringType extension
= FILE_PATH_LITERAL(".skp");
115 base::FileEnumerator
file_iter(input_path
, false,
116 base::FileEnumerator::FILES
);
117 while (!file_iter
.Next().empty()) {
118 if (file_iter
.GetInfo().GetName().MatchesExtension(extension
)) {
119 base::FilePath skp_file
= file_iter
.GetInfo().GetName();
120 skp_file
= input_path
.AsEndingWithSeparator().Append(skp_file
);
121 base::FilePath png_file
;
122 if (!output_path
.empty())
123 png_file
= MakeDestinationPNGFilename(skp_file
, output_path
);
124 files
.push_back(std::make_pair(skp_file
, png_file
));
128 // Single file passed. If the output file is a folder, make a name.
129 if (base::DirectoryExists(output_path
))
130 output_path
= MakeDestinationPNGFilename(input_path
, output_path
);
131 files
.push_back(std::make_pair(input_path
, output_path
));
136 int GetSwitchInt(const base::CommandLine
* command_line
,
137 std::string switch_name
,
139 if (command_line
->HasSwitch(switch_name
)) {
140 std::string value
= command_line
->GetSwitchValueASCII(switch_name
);
141 int result
= default_value
;
142 if (base::StringToInt(value
, &result
))
145 return default_value
;
148 static const char kHelpMessage
[] =
149 "This program renders a skia SKP to a PNG using GPU rasterization via the\n"
150 "command buffer.\n\n"
151 "following command line flags to control its behavior:\n"
153 " --in-skp=skp[:DIRECTORY_PATH|FILE_PATH]\n"
154 " Input SKP file. If a directory is provided, all SKP files will be\n"
156 " --out-png=png[:DIRECTORY_PATH|:FILE_PATH]\n"
157 " Output PNG file. If a directory is provided, the SKP filename is "
160 " Turn on lcd text rendering.\n"
161 " --use-distance-field-text\n"
162 " Turn on distance field text rendering.\n"
163 " --msaa-sample-count=(0|2|4|8|16)\n"
164 " Turn on multi-sample anti-aliasing.\n"
165 " --use-gl=(desktop|osmesa|egl|swiftshader)\n"
166 " Specify Gl driver. --swiftshader-path required for swiftshader.\n"
167 " --repeat-raster-count=N\n"
168 " Specify the number of times to repeat rasterization for timing.\n";
170 } // namespace anonymous
172 int main(int argc
, char** argv
) {
173 base::AtExitManager exit_manager
;
174 base::CommandLine::Init(argc
, argv
);
175 #if defined(OS_MACOSX)
176 base::mac::ScopedNSAutoreleasePool autorelease_pool
;
178 base::CommandLine
* command_line
= base::CommandLine::ForCurrentProcess();
179 base::FilePath
input_path(command_line
->GetSwitchValuePath("in-skp"));
180 base::FilePath
output_path(command_line
->GetSwitchValuePath("out-png"));
182 if (input_path
.empty()) {
183 std::cout
<< kHelpMessage
;
187 if (output_path
.empty())
188 std::cout
<< "--out-png path not specified. Timing rasterization only.\n";
190 std::vector
<std::pair
<base::FilePath
, base::FilePath
>> files
=
191 GetSkpsToRasterize(input_path
, output_path
);
194 if (!base::DirectoryExists(output_path
))
195 std::cout
<< "You must specify an existing directory using '--out-png'\n";
197 std::cout
<< "No skp files found at " << input_path
.value() << "\n";
201 skia_runner::InProcessGraphicsSystem graphics_system
;
202 if (!graphics_system
.IsSuccessfullyInitialized()) {
203 LOG(ERROR
) << "Unable to initialize rasterizer.";
207 skia_runner::SkPictureRasterizer
picture_rasterizer(
208 graphics_system
.GetGrContext(), graphics_system
.GetMaxTextureSize());
210 // Set up command-line render options.
211 picture_rasterizer
.set_use_lcd_text(command_line
->HasSwitch("use-lcd-text"));
212 picture_rasterizer
.set_use_distance_field_text(
213 command_line
->HasSwitch("use-distance-field-text"));
215 int msaa
= GetSwitchInt(command_line
, "msaa-sample-count", 0);
216 if (msaa
!= 0 && msaa
!= 2 && msaa
!= 4 && msaa
!= 8 && msaa
!= 16) {
217 std::cout
<< "Error: msaa sample count must be 0, 2, 4, 8 or 16.\n";
220 picture_rasterizer
.set_msaa_sample_count(msaa
);
222 int repeat_raster_count
=
223 std::max(1, GetSwitchInt(command_line
, "repeat-raster-count", 1));
225 // Disable the security precautions to ensure we correctly read the picture.
226 SkPicture::SetPictureIOSecurityPrecautionsEnabled_Dangerous(false);
228 for (auto file_pair
: files
) {
229 skia::RefPtr
<SkPicture
> picture
= ReadPicture(file_pair
.first
);
231 std::cout
<< "Error reading: " << file_pair
.first
.value() << "\n";
235 // GPU Rasterize the picture and time it.
236 base::TimeTicks rasterize_start
= base::TimeTicks::Now();
237 skia::RefPtr
<SkImage
> image
;
238 for (int i
= 0; i
< repeat_raster_count
; ++i
) {
239 image
= picture_rasterizer
.Rasterize(picture
.get());
240 graphics_system
.GetGrContext()->flush();
242 base::TimeDelta rasterize_time
= base::TimeTicks::Now() - rasterize_start
;
243 double average_time
= rasterize_time
.InSecondsF() / repeat_raster_count
;
244 std::cout
<< "Average rasterization time for " << file_pair
.first
.value()
245 << ": " << average_time
<< "s (" << 1.0 / average_time
248 if (!file_pair
.second
.empty()) {
249 if (!WriteSkImagePNG(image
.get(), file_pair
.second
))
250 std::cout
<< "Error writing: " << file_pair
.second
.value() << "\n";
252 std::cout
<< file_pair
.second
.value() << " successfully created.\n";