1// Aseprite
2// Copyright (C) 2019-2021 Igara Studio S.A.
3//
4// This program is distributed under the terms of
5// the End-User License Agreement for Aseprite.
6
7#ifdef HAVE_CONFIG_H
8#include "config.h"
9#endif
10
11#include "app/app.h"
12#include "app/cmd/convert_color_profile.h"
13#include "app/commands/command.h"
14#include "app/commands/new_params.h"
15#include "app/context.h"
16#include "app/doc.h"
17#include "app/file/file.h"
18#include "app/i18n/strings.h"
19#include "app/resource_finder.h"
20#include "base/buffer.h"
21#include "base/fs.h"
22#include "doc/cel.h"
23#include "doc/color.h"
24#include "doc/image_impl.h"
25#include "doc/layer.h"
26#include "doc/sprite.h"
27#include "fmt/format.h"
28#include "os/surface.h"
29#include "os/window.h"
30#include "ui/alert.h"
31#include "ui/manager.h"
32#include "ui/scale.h"
33
34#ifdef ENABLE_STEAM
35 #include "steam/steam.h"
36#endif
37
38namespace app {
39
40using namespace ui;
41
42struct ScreenshotParams : public NewParams {
43 Param<bool> save { this, false, "save" };
44 Param<bool> srgb { this, true, "srgb" };
45#ifdef ENABLE_STEAM
46 Param<bool> steam { this, false, "steam" };
47#endif
48};
49
50class ScreenshotCommand : public CommandWithNewParams<ScreenshotParams> {
51public:
52 ScreenshotCommand();
53
54protected:
55 void onExecute(Context* ctx) override;
56 std::string onGetFriendlyName() const override;
57};
58
59ScreenshotCommand::ScreenshotCommand()
60 : CommandWithNewParams<ScreenshotParams>(CommandId::Screenshot(), CmdUIOnlyFlag)
61{
62}
63
64void ScreenshotCommand::onExecute(Context* ctx)
65{
66 if (!ctx->isUIAvailable())
67 return;
68
69 app::ResourceFinder rf(false);
70 rf.includeDesktopDir("");
71
72 os::Window* window = ui::Manager::getDefault()->display()->nativeWindow();
73 os::Surface* surface = window->surface();
74 std::string fn;
75
76 if (params().save()) {
77 for (int i=0; i<10000; ++i) {
78 fn = base::join_path(rf.defaultFilename(),
79 fmt::format("screenshot-{}.png", i));
80 if (!base::is_file(fn))
81 break;
82 }
83 }
84 else {
85 fn = "screenshot.png";
86 }
87
88 doc::ImageSpec spec(doc::ColorMode::RGB,
89 surface->width(),
90 surface->height(),
91 0, // Mask color
92 window->colorSpace()->gfxColorSpace());
93
94 doc::Sprite* spr = doc::Sprite::MakeStdSprite(spec);
95 static_cast<doc::LayerImage*>(spr->firstLayer())->configureAsBackground();
96
97 doc::Cel* cel = spr->firstLayer()->cel(0);
98 doc::Image* img = cel->image();
99 const int w = img->width();
100 const int h = img->height();
101
102 for (int y=0; y<h; ++y) {
103 for (int x=0; x<w; ++x) {
104 gfx::Color c = surface->getPixel(x, y);
105
106 img->putPixel(x, y, doc::rgba(gfx::getr(c),
107 gfx::getg(c),
108 gfx::getb(c), 255));
109 }
110 }
111
112 std::unique_ptr<Doc> doc(new Doc(spr));
113 doc->setFilename(fn);
114
115 // Convert color profile to sRGB
116 if (params().srgb())
117 cmd::convert_color_profile(spr, gfx::ColorSpace::MakeSRGB());
118
119#ifdef ENABLE_STEAM
120 if (params().steam()) {
121 if (auto steamAPI = steam::SteamAPI::instance()) {
122 // Get image again (cmd::convert_color_profile() might have changed it)
123 img = cel->image();
124
125 const int scale = window->scale();
126 base::buffer rgbBuffer(3*w*h*scale*scale);
127 int c = 0;
128 doc::LockImageBits<RgbTraits> bits(img);
129 for (int y=0; y<h; ++y) {
130 for (int i=0; i<scale; ++i) {
131 for (int x=0; x<w; ++x) {
132 color_t color = get_pixel_fast<RgbTraits>(img, x, y);
133 for (int j=0; j<scale; ++j) {
134 rgbBuffer[c++] = doc::rgba_getr(color);
135 rgbBuffer[c++] = doc::rgba_getg(color);
136 rgbBuffer[c++] = doc::rgba_getb(color);
137 }
138 }
139 }
140 }
141 if (steamAPI->writeScreenshot(&rgbBuffer[0], rgbBuffer.size(), w*scale, h*scale))
142 return;
143 }
144 }
145#endif
146
147 if (params().save()) {
148 save_document(ctx, doc.get());
149 }
150 else {
151 doc->setContext(ctx);
152 doc.release();
153 }
154}
155
156std::string ScreenshotCommand::onGetFriendlyName() const
157{
158 std::string name;
159#ifdef ENABLE_STEAM
160 if (params().steam())
161 name = Strings::commands_Screenshot_Steam();
162 else
163#endif
164 if (params().save())
165 name = Strings::commands_Screenshot_Save();
166 else
167 name = Strings::commands_Screenshot_Open();
168 if (params().srgb()) {
169 name.push_back(' ');
170 name += Strings::commands_Screenshot_sRGB();
171 }
172 else {
173 name.push_back(' ');
174 name += Strings::commands_Screenshot_DisplayCS();
175 }
176 return name;
177}
178
179Command* CommandFactory::createScreenshotCommand()
180{
181 return new ScreenshotCommand;
182}
183
184} // namespace app
185