1 /*
2  * This file is part of EasyRPG Player.
3  *
4  * EasyRPG Player is free software: you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation, either version 3 of the License, or
7  * (at your option) any later version.
8  *
9  * EasyRPG Player is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with EasyRPG Player. If not, see <http://www.gnu.org/licenses/>.
16  */
17 
18 // Headers
19 #define _USE_MATH_DEFINES
20 #include <cmath>
21 #include <cstdlib>
22 #include <cstring>
23 #include <algorithm>
24 #include <iostream>
25 #include <unordered_map>
26 
27 #include "utils.h"
28 #include "cache.h"
29 #include "bitmap.h"
30 #include "filefinder.h"
31 #include "options.h"
32 #include <lcf/data.h>
33 #include "output.h"
34 #include "image_xyz.h"
35 #include "image_bmp.h"
36 #include "image_png.h"
37 #include "transform.h"
38 #include "font.h"
39 #include "output.h"
40 #include "util_macro.h"
41 #include "bitmap_hslrgb.h"
42 #include <iostream>
43 
Create(int width,int height,const Color & color)44 BitmapRef Bitmap::Create(int width, int height, const Color& color) {
45 	BitmapRef surface = Bitmap::Create(width, height, true);
46 	surface->Fill(color);
47 	return surface;
48 }
49 
Create(Filesystem_Stream::InputStream stream,bool transparent,uint32_t flags)50 BitmapRef Bitmap::Create(Filesystem_Stream::InputStream stream, bool transparent, uint32_t flags) {
51 	BitmapRef bmp = std::make_shared<Bitmap>(std::move(stream), transparent, flags);
52 
53 	if (!bmp->pixels()) {
54 		return BitmapRef();
55 	}
56 
57 	return bmp;
58 }
59 
Create(const uint8_t * data,unsigned bytes,bool transparent,uint32_t flags)60 BitmapRef Bitmap::Create(const uint8_t* data, unsigned bytes, bool transparent, uint32_t flags) {
61 	BitmapRef bmp = std::make_shared<Bitmap>(data, bytes, transparent, flags);
62 
63 	if (!bmp->pixels()) {
64 		return BitmapRef();
65 	}
66 
67 	return bmp;
68 }
69 
Create(Bitmap const & source,Rect const & src_rect,bool transparent)70 BitmapRef Bitmap::Create(Bitmap const& source, Rect const& src_rect, bool transparent) {
71 	return std::make_shared<Bitmap>(source, src_rect, transparent);
72 }
73 
Create(int width,int height,bool transparent,int)74 BitmapRef Bitmap::Create(int width, int height, bool transparent, int /* bpp */) {
75 	return std::make_shared<Bitmap>(width, height, transparent);
76 }
77 
Create(void * pixels,int width,int height,int pitch,const DynamicFormat & format)78 BitmapRef Bitmap::Create(void *pixels, int width, int height, int pitch, const DynamicFormat& format) {
79 	return std::make_shared<Bitmap>(pixels, width, height, pitch, format);
80 }
81 
Bitmap(int width,int height,bool transparent)82 Bitmap::Bitmap(int width, int height, bool transparent) {
83 	format = (transparent ? pixel_format : opaque_pixel_format);
84 	pixman_format = find_format(format);
85 	Init(width, height, (void *) NULL);
86 }
87 
Bitmap(void * pixels,int width,int height,int pitch,const DynamicFormat & _format)88 Bitmap::Bitmap(void *pixels, int width, int height, int pitch, const DynamicFormat& _format) {
89 	format = _format;
90 	pixman_format = find_format(format);
91 	Init(width, height, pixels, pitch, false);
92 }
93 
Bitmap(Filesystem_Stream::InputStream stream,bool transparent,uint32_t flags)94 Bitmap::Bitmap(Filesystem_Stream::InputStream stream, bool transparent, uint32_t flags) {
95 	format = (transparent ? pixel_format : opaque_pixel_format);
96 	pixman_format = find_format(format);
97 
98 	if (!stream) {
99 		Output::Error("Couldn't read image file {}", stream.GetName());
100 		return;
101 	}
102 
103 	int w = 0;
104 	int h = 0;
105 	void* pixels = nullptr;
106 
107 	uint8_t data[4] = {};
108 	size_t bytes = stream.read(reinterpret_cast<char*>(data),  4).gcount();
109 	stream.seekg(0, std::ios::ios_base::beg);
110 
111 	bool img_okay = false;
112 
113 	if (bytes >= 4 && strncmp((char*)data, "XYZ1", 4) == 0)
114 		img_okay = ImageXYZ::ReadXYZ(stream, transparent, w, h, pixels);
115 	else if (bytes > 2 && strncmp((char*)data, "BM", 2) == 0)
116 		img_okay = ImageBMP::ReadBMP(stream, transparent, w, h, pixels);
117 	else if (bytes >= 4 && strncmp((char*)(data + 1), "PNG", 3) == 0)
118 		img_okay = ImagePNG::ReadPNG(stream, transparent, w, h, pixels);
119 	else
120 		Output::Warning("Unsupported image file {} (Magic: {:02X})", stream.GetName(), *reinterpret_cast<uint32_t*>(data));
121 
122 	if (!img_okay) {
123 		free(pixels);
124 		pixels = nullptr;
125 		return;
126 	}
127 
128 	Init(w, h, nullptr);
129 
130 	ConvertImage(w, h, pixels, transparent);
131 
132 	CheckPixels(flags);
133 
134 	filename = ToString(stream.GetName());
135 }
136 
Bitmap(const uint8_t * data,unsigned bytes,bool transparent,uint32_t flags)137 Bitmap::Bitmap(const uint8_t* data, unsigned bytes, bool transparent, uint32_t flags) {
138 	format = (transparent ? pixel_format : opaque_pixel_format);
139 	pixman_format = find_format(format);
140 
141 	int w = 0, h = 0;
142 	void* pixels = nullptr;
143 
144 	bool img_okay = false;
145 
146 	if (bytes > 4 && strncmp((char*) data, "XYZ1", 4) == 0)
147 		img_okay = ImageXYZ::ReadXYZ(data, bytes, transparent, w, h, pixels);
148 	else if (bytes > 2 && strncmp((char*) data, "BM", 2) == 0)
149 		img_okay = ImageBMP::ReadBMP(data, bytes, transparent, w, h, pixels);
150 	else if (bytes > 4 && strncmp((char*)(data + 1), "PNG", 3) == 0)
151 		img_okay = ImagePNG::ReadPNG((const void*) data, transparent, w, h, pixels);
152 	else
153 		Output::Warning("Unsupported image (Magic: {:02X})", bytes >= 4 ? *reinterpret_cast<const uint32_t*>(data) : 0);
154 
155 	if (!img_okay) {
156 		free(pixels);
157 		pixels = nullptr;
158 		return;
159 	}
160 
161 	Init(w, h, nullptr);
162 
163 	ConvertImage(w, h, pixels, transparent);
164 
165 	CheckPixels(flags);
166 }
167 
Bitmap(Bitmap const & source,Rect const & src_rect,bool transparent)168 Bitmap::Bitmap(Bitmap const& source, Rect const& src_rect, bool transparent) {
169 	format = (transparent ? pixel_format : opaque_pixel_format);
170 	pixman_format = find_format(format);
171 
172 	Init(src_rect.width, src_rect.height, (void *) NULL);
173 
174 	Blit(0, 0, source, src_rect, Opacity::Opaque());
175 }
176 
WritePNG(Filesystem_Stream::OutputStream & os) const177 bool Bitmap::WritePNG(Filesystem_Stream::OutputStream& os) const {
178 	size_t const width = GetWidth(), height = GetHeight();
179 	size_t const stride = width * 4;
180 
181 	std::vector<uint32_t> data(width * height);
182 
183 	auto dst = PixmanImagePtr{pixman_image_create_bits(PIXMAN_b8g8r8, width, height, &data.front(), stride)};
184 	pixman_image_composite32(PIXMAN_OP_SRC, bitmap.get(), NULL, dst.get(),
185 							 0, 0, 0, 0, 0, 0, width, height);
186 
187 	return ImagePNG::WritePNG(os, width, height, &data.front());
188 }
189 
GetSize() const190 size_t Bitmap::GetSize() const {
191 	if (!bitmap) {
192 		return 0;
193 	}
194 
195 	return pitch() * height();
196 }
197 
ComputeImageOpacity() const198 ImageOpacity Bitmap::ComputeImageOpacity() const {
199 	bool all_opaque = true;
200 	bool all_transp = true;
201 
202 	auto* p = reinterpret_cast<const uint32_t*>(pixels());
203 	const auto mask = pixel_format.rgba_to_uint32_t(0, 0, 0, 0xFF);
204 
205 	int n = GetSize() / sizeof(uint32_t);
206 	for (int i = 0; i < n; ++i ) {
207 		auto px = p[i] & mask;
208 		all_opaque &= (px == mask);
209 		all_transp &= (px == 0);
210 	}
211 
212 	return
213 		all_transp ? ImageOpacity::Transparent :
214 		all_opaque ? ImageOpacity::Opaque :
215 		ImageOpacity::Partial;
216 }
217 
ComputeImageOpacity(Rect rect) const218 ImageOpacity Bitmap::ComputeImageOpacity(Rect rect) const {
219 	bool all_opaque = true;
220 	bool all_transp = true;
221 
222 	const auto full_rect = GetRect();
223 	rect = full_rect.GetSubRect(rect);
224 
225 	auto* p = reinterpret_cast<const uint32_t*>(pixels());
226 	const int stride = pitch() / sizeof(uint32_t);
227 	const auto mask = pixel_format.rgba_to_uint32_t(0, 0, 0, 0xFF);
228 
229 	int xend = (rect.x + rect.width);
230 	int yend = (rect.y + rect.height);
231 	for (int y = rect.y * stride; y < yend * stride; y += stride) {
232 		for (int x = rect.x; x < xend; ++x) {
233 			auto px = p[x + y] & mask;
234 			all_transp &= (px == 0);
235 			all_opaque &= (px == mask);
236 		}
237 	}
238 
239 	return
240 		all_transp ? ImageOpacity::Transparent :
241 		all_opaque ? ImageOpacity::Opaque :
242 		ImageOpacity::Partial;
243 }
244 
CheckPixels(uint32_t flags)245 void Bitmap::CheckPixels(uint32_t flags) {
246 	if (flags & Flag_System) {
247 		DynamicFormat format(32,8,24,8,16,8,8,8,0,PF::Alpha);
248 		uint32_t pixel;
249 		Bitmap bmp(reinterpret_cast<void*>(&pixel), 1, 1, 4, format);
250 		pixman_image_composite32(PIXMAN_OP_SRC, bitmap.get(), (pixman_image_t*) NULL, bmp.bitmap.get(),
251 								 0, 32,  0, 0,  0, 0,  1, 1);
252 		bg_color = Color((int)(pixel>>24)&0xFF, (int)(pixel>>16)&0xFF, (int)(pixel>>8)&0xFF, (int)pixel&0xFF);
253 		pixman_image_composite32(PIXMAN_OP_SRC, bitmap.get(), (pixman_image_t*) NULL, bmp.bitmap.get(),
254 								 16, 32,  0, 0,  0, 0,  1, 1);
255 		sh_color = Color((int)(pixel>>24)&0xFF, (int)(pixel>>16)&0xFF, (int)(pixel>>8)&0xFF, (int)pixel&0xFF);
256 	}
257 
258 	if (flags & Flag_Chipset) {
259 		const int h = height() / TILE_SIZE;
260 		const int w = width() / TILE_SIZE;
261 		tile_opacity = TileOpacity(w, h);
262 
263 		for (int ty = 0; ty < h; ++ty) {
264 			for (int tx = 0; tx < w; ++tx) {
265 				Rect rect(tx * TILE_SIZE, ty * TILE_SIZE, TILE_SIZE, TILE_SIZE);
266 				auto op = ComputeImageOpacity(rect);
267 				tile_opacity.Set(tx, ty, op);
268 			}
269 		}
270 	}
271 
272 	if (flags & Flag_ReadOnly) {
273 		read_only = true;
274 
275 		image_opacity = ComputeImageOpacity();
276 	}
277 }
278 
HueChangeBlit(int x,int y,Bitmap const & src,Rect const & src_rect_,double hue_)279 void Bitmap::HueChangeBlit(int x, int y, Bitmap const& src, Rect const& src_rect_, double hue_) {
280 	Rect dst_rect(x, y, 0, 0), src_rect = src_rect_;
281 
282 	if (!Rect::AdjustRectangles(src_rect, dst_rect, src.GetRect()))
283 		return;
284 	if (!Rect::AdjustRectangles(dst_rect, src_rect, GetRect()))
285 		return;
286 
287 	int hue  = (int) (hue_ / 60.0 * 0x100);
288 	if (hue < 0)
289 		hue += ((-hue + 0x5FF) / 0x600) * 0x600;
290 	else if (hue > 0x600)
291 		hue -= (hue / 0x600) * 0x600;
292 
293 	DynamicFormat format(32,8,24,8,16,8,8,8,0,PF::Alpha);
294 	std::vector<uint32_t> pixels;
295 	pixels.resize(src_rect.width * src_rect.height);
296 	Bitmap bmp(reinterpret_cast<void*>(&pixels.front()), src_rect.width, src_rect.height, src_rect.width * 4, format);
297 	bmp.Blit(0, 0, src, src_rect, Opacity::Opaque());
298 
299 	for (std::vector<uint32_t>::iterator p = pixels.begin(); p != pixels.end(); ++p) {
300 		uint32_t pixel = *p;
301 		uint8_t r = (pixel>>24) & 0xFF;
302 		uint8_t g = (pixel>>16) & 0xFF;
303 		uint8_t b = (pixel>> 8) & 0xFF;
304 		uint8_t a = pixel & 0xFF;
305 		if (a > 0)
306 			RGB_adjust_HSL(r, g, b, hue);
307 		*p = ((uint32_t) r << 24) | ((uint32_t) g << 16) | ((uint32_t) b << 8) | (uint32_t) a;
308 	}
309 
310 	Blit(dst_rect.x, dst_rect.y, bmp, bmp.GetRect(), Opacity::Opaque());
311 }
312 
TextDraw(Rect const & rect,int color,StringView text,Text::Alignment align)313 void Bitmap::TextDraw(Rect const& rect, int color, StringView text, Text::Alignment align) {
314 	FontRef font = Font::Default();
315 	Rect text_rect = font->GetSize(text);
316 	int dx = text_rect.width - rect.width;
317 
318 	switch (align) {
319 	case Text::AlignLeft:
320 		TextDraw(rect.x, rect.y, color, text);
321 		break;
322 	case Text::AlignCenter:
323 		TextDraw(rect.x + dx / 2, rect.y, color, text);
324 		break;
325 	case Text::AlignRight:
326 		TextDraw(rect.x + dx, rect.y, color, text);
327 		break;
328 	default: assert(false);
329 	}
330 }
331 
TextDraw(int x,int y,int color,StringView text,Text::Alignment align)332 void Bitmap::TextDraw(int x, int y, int color, StringView text, Text::Alignment align) {
333 	auto font = Font::Default();
334 	auto system = Cache::SystemOrBlack();
335 	Text::Draw(*this, x, y, *font, *system, color, text, align);
336 }
337 
TextDraw(Rect const & rect,Color color,StringView text,Text::Alignment align)338 void Bitmap::TextDraw(Rect const& rect, Color color, StringView text, Text::Alignment align) {
339 	FontRef font = Font::Default();
340 	Rect text_rect = font->GetSize(text);
341 	int dx = text_rect.width - rect.width;
342 
343 	switch (align) {
344 	case Text::AlignLeft:
345 		TextDraw(rect.x, rect.y, color, text);
346 		break;
347 	case Text::AlignCenter:
348 		TextDraw(rect.x + dx / 2, rect.y, color, text);
349 		break;
350 	case Text::AlignRight:
351 		TextDraw(rect.x + dx, rect.y, color, text);
352 		break;
353 	default: assert(false);
354 	}
355 }
356 
TextDraw(int x,int y,Color color,StringView text)357 void Bitmap::TextDraw(int x, int y, Color color, StringView text) {
358 	auto font = Font::Default();
359 	Text::Draw(*this, x, y, *font, color, text);
360 }
361 
TransformRectangle(const Transform & xform,const Rect & rect)362 Rect Bitmap::TransformRectangle(const Transform& xform, const Rect& rect) {
363 	pixman_box16 bounds = {
364 		static_cast<int16_t>(rect.x),
365 		static_cast<int16_t>(rect.y),
366 		static_cast<int16_t>(rect.x + rect.width),
367 		static_cast<int16_t>(rect.y + rect.height)
368 	};
369 
370 	pixman_transform_bounds(&xform.matrix, &bounds);
371 	return Rect(bounds.x1, bounds.y1, bounds.x2 - bounds.x1, bounds.y2 - bounds.y1);
372 }
373 
374 static constexpr std::array<std::pair<int,pixman_format_code_t>, 27> formats_map = {{
375 		{ DynamicFormat(32,8,24,8,16,8,8,8,0,PF::Alpha).code_alpha(), PIXMAN_r8g8b8a8 },
376 		{ DynamicFormat(32,8,24,8,16,8,8,8,0,PF::NoAlpha).code_alpha(), PIXMAN_r8g8b8x8 },
377 
378 		{ DynamicFormat(32,8,16,8,8,8,0,8,24,PF::Alpha).code_alpha(), PIXMAN_a8r8g8b8 },
379 		{ DynamicFormat(32,8,16,8,8,8,0,0,0,PF::NoAlpha).code_alpha(), PIXMAN_x8r8g8b8 },
380 		{ DynamicFormat(32,8,0,8,8,8,16,8,24,PF::Alpha).code_alpha(), PIXMAN_a8b8g8r8 },
381 		{ DynamicFormat(32,8,0,8,8,8,16,0,0,PF::NoAlpha).code_alpha(), PIXMAN_x8b8g8r8 },
382 		{ DynamicFormat(32,8,8,8,16,8,24,8,0,PF::Alpha).code_alpha(), PIXMAN_b8g8r8a8 },
383 		{ DynamicFormat(32,8,8,8,16,8,24,0,0,PF::NoAlpha).code_alpha(), PIXMAN_b8g8r8x8 },
384 
385 		{ DynamicFormat(32,6,12,6,6,6,0,0,0,PF::NoAlpha).code_alpha(), PIXMAN_x14r6g6b6 },
386 		{ DynamicFormat(32,10,20,10,10,10,0,0,0,PF::NoAlpha).code_alpha(), PIXMAN_x2r10g10b10 },
387 		{ DynamicFormat(32,10,20,10,10,10,0,2,30,PF::Alpha).code_alpha(), PIXMAN_a2r10g10b10 },
388 		{ DynamicFormat(32,10,0,10,10,10,20,0,0,PF::NoAlpha).code_alpha(), PIXMAN_x2b10g10r10 },
389 		{ DynamicFormat(32,10,0,10,10,10,20,2,30,PF::Alpha).code_alpha(), PIXMAN_a2b10g10r10 },
390 
391 		{ DynamicFormat(24,8,16,8,8,8,0,0,0,PF::NoAlpha).code_alpha(), PIXMAN_r8g8b8 },
392 		{ DynamicFormat(24,8,0,8,8,8,16,0,0,PF::NoAlpha).code_alpha(), PIXMAN_b8g8r8 },
393 
394 		{ DynamicFormat(16,5,11,6,5,5,0,0,0,PF::NoAlpha).code_alpha(), PIXMAN_r5g6b5 },
395 		{ DynamicFormat(16,5,0,6,5,5,11,0,0,PF::NoAlpha).code_alpha(), PIXMAN_b5g6r5 },
396 		{ DynamicFormat(16,5,10,5,5,5,0,1,15,PF::Alpha).code_alpha(), PIXMAN_a1r5g5b5 },
397 		{ DynamicFormat(16,5,10,5,5,5,0,0,0,PF::NoAlpha).code_alpha(), PIXMAN_x1r5g5b5 },
398 		{ DynamicFormat(16,5,0,5,5,5,10,1,15,PF::Alpha).code_alpha(), PIXMAN_a1b5g5r5 },
399 		{ DynamicFormat(16,5,0,5,5,5,10,0,0,PF::NoAlpha).code_alpha(), PIXMAN_x1b5g5r5 },
400 		{ DynamicFormat(16,4,8,4,4,4,0,4,12,PF::Alpha).code_alpha(), PIXMAN_a4r4g4b4 },
401 		{ DynamicFormat(16,4,8,4,4,4,0,0,0,PF::NoAlpha).code_alpha(), PIXMAN_x4r4g4b4 },
402 		{ DynamicFormat(16,4,0,4,4,4,8,4,12,PF::Alpha).code_alpha(), PIXMAN_a4b4g4r4 },
403 		{ DynamicFormat(16,4,0,4,4,4,8,0,0,PF::NoAlpha).code_alpha(), PIXMAN_x4b4g4r4 },
404 		{ DynamicFormat(8,8,0,8,0,8,0,8,0,PF::Alpha).code_alpha(), PIXMAN_g8 },
405 		{ DynamicFormat(8,8,0,8,0,8,0,0,0,PF::NoAlpha).code_alpha(), PIXMAN_g8 }
406 }};
407 
find_format(const DynamicFormat & format)408 pixman_format_code_t Bitmap::find_format(const DynamicFormat& format) {
409 	auto dcode = format.code_alpha();
410 	auto iter = std::find_if(formats_map.begin(), formats_map.end(), [dcode](const auto& p) { return p.first == dcode; });
411 	if (iter == formats_map.end()) {
412 		// To fix add a pair to initialize_formats that maps the outputted
413 		// DynamicFormat to a pixman format
414 		Output::Error("{}\nDynamicFormat({}, {}, {}, {}, {}, {}, {}, {}, {}, {})",
415 		"Couldn't find Pixman format for",
416 		format.bits,
417 		format.r.bits, format.r.shift,
418 		format.g.bits, format.g.shift,
419 		format.b.bits, format.b.shift,
420 		format.a.bits, format.a.shift,
421 		format.alpha_type == PF::Alpha ? "PF::Alpha" : "PF::NoAlpha");
422 	}
423 	return iter->second;
424 }
425 
426 DynamicFormat Bitmap::pixel_format;
427 DynamicFormat Bitmap::opaque_pixel_format;
428 DynamicFormat Bitmap::image_format;
429 DynamicFormat Bitmap::opaque_image_format;
430 
SetFormat(const DynamicFormat & format)431 void Bitmap::SetFormat(const DynamicFormat& format) {
432 	pixel_format = format;
433 	opaque_pixel_format = format;
434 	opaque_pixel_format.alpha_type = PF::NoAlpha;
435 	image_format = format_R8G8B8A8_a().format();
436 	opaque_image_format = format_R8G8B8A8_n().format();
437 }
438 
ChooseFormat(const DynamicFormat & format)439 DynamicFormat Bitmap::ChooseFormat(const DynamicFormat& format) {
440 	uint32_t amask;
441 	amask = (format.a.mask == 0)
442 		? ((~0U >> (32 - format.bits)) ^ (format.r.mask | format.g.mask | format.b.mask))
443 		: format.a.mask;
444 	if (amask != 0)
445 		return DynamicFormat(format.bits,
446 							 format.r.mask, format.g.mask, format.b.mask,
447 							 amask, PF::Alpha);
448 	switch (format.bits) {
449 		case 16:
450 			return (format.r.shift > format.b.shift)
451 				? DynamicFormat(16,5,10,5,5,5,0,1,15,PF::Alpha)
452 				: DynamicFormat(16,5,0,5,5,5,10,1,15,PF::Alpha);
453 		case 24:
454 			return (format.r.shift > format.b.shift)
455 				? DynamicFormat(32,8,16,8,8,8,0,8,24,PF::Alpha)
456 				: DynamicFormat(32,8,0,8,8,8,16,8,24,PF::Alpha);
457 		default:
458 			return format_B8G8R8A8_a().format();
459 	}
460 }
461 
destroy_func(pixman_image_t *,void * data)462 static void destroy_func(pixman_image_t * /* image */, void *data) {
463 	free(data);
464 }
465 
466 static pixman_indexed_t palette;
467 static bool palette_initialized = false;
468 
initialize_palette()469 static void initialize_palette() {
470 	if (palette_initialized)
471 		return;
472 	palette.color = false;
473 	palette.rgba[0] = 0U;
474 	for (int i = 1; i < PIXMAN_MAX_INDEXED; i++)
475 		palette.rgba[i] = ~0U;
476 	palette_initialized = true;
477 }
478 
Init(int width,int height,void * data,int pitch,bool destroy)479 void Bitmap::Init(int width, int height, void* data, int pitch, bool destroy) {
480 	if (!pitch)
481 		pitch = width * format.bytes;
482 
483 	bitmap.reset(pixman_image_create_bits(pixman_format, width, height, (uint32_t*) data, pitch));
484 
485 	if (bitmap == NULL) {
486 		Output::Error("Couldn't create {}x{} image.", width, height);
487 	}
488 
489 	if (format.bits == 8) {
490 		initialize_palette();
491 		pixman_image_set_indexed(bitmap.get(), &palette);
492 	}
493 
494 	if (data != NULL && destroy)
495 		pixman_image_set_destroy_function(bitmap.get(), destroy_func, data);
496 }
497 
ConvertImage(int & width,int & height,void * & pixels,bool transparent)498 void Bitmap::ConvertImage(int& width, int& height, void*& pixels, bool transparent) {
499 	const DynamicFormat& img_format = transparent ? image_format : opaque_image_format;
500 
501 	// premultiply alpha
502 	for (int y = 0; y < height; y++) {
503 		uint8_t* dst = (uint8_t*) pixels + y * width * 4;
504 		for (int x = 0; x < width; x++) {
505 			uint8_t &r = *dst++;
506 			uint8_t &g = *dst++;
507 			uint8_t &b = *dst++;
508 			uint8_t &a = *dst++;
509 			MultiplyAlpha(r, g, b, a);
510 		}
511 	}
512 
513 	Bitmap src(pixels, width, height, 0, img_format);
514 	Clear();
515 	Blit(0, 0, src, src.GetRect(), Opacity::Opaque());
516 	free(pixels);
517 }
518 
pixels()519 void* Bitmap::pixels() {
520 	if (!bitmap) {
521 		return nullptr;
522 	}
523 
524 	return (void*) pixman_image_get_data(bitmap.get());
525 }
pixels() const526 void const* Bitmap::pixels() const {
527 	return (void const*) pixman_image_get_data(bitmap.get());
528 }
529 
bpp() const530 int Bitmap::bpp() const {
531 	return (pixman_image_get_depth(bitmap.get()) + 7) / 8;
532 }
533 
width() const534 int Bitmap::width() const {
535 	return pixman_image_get_width(bitmap.get());
536 }
537 
height() const538 int Bitmap::height() const {
539 	return pixman_image_get_height(bitmap.get());
540 }
541 
pitch() const542 int Bitmap::pitch() const {
543 	return pixman_image_get_stride(bitmap.get());
544 }
545 
546 namespace {
CreateMask(Opacity const & opacity,Rect const & src_rect,Transform const * pxform=nullptr)547 	PixmanImagePtr CreateMask(Opacity const& opacity, Rect const& src_rect, Transform const* pxform = nullptr) {
548 		if (opacity.IsOpaque()) {
549 			return nullptr;
550 		}
551 
552 		if (!opacity.IsSplit()) {
553 			pixman_color_t tcolor = {0, 0, 0, static_cast<uint16_t>(opacity.Value() << 8)};
554 			return PixmanImagePtr{ pixman_image_create_solid_fill(&tcolor) };
555 		}
556 
557 		auto mask = PixmanImagePtr{pixman_image_create_bits(PIXMAN_a8, 1, 2, (uint32_t*) NULL, 4)};
558 		uint32_t* pixels = pixman_image_get_data(mask.get());
559 		*reinterpret_cast<uint8_t*>(&pixels[0]) = (opacity.top & 0xFF);
560 		*reinterpret_cast<uint8_t*>(&pixels[1]) = (opacity.bottom & 0xFF);
561 
562 		Transform xform = Transform::Scale(1.0 / src_rect.width, 1.0 / src_rect.height);
563 		xform *= Transform::Translation(0, opacity.split);
564 
565 		if (pxform)
566 			xform *= *pxform;
567 
568 		pixman_image_set_transform(mask.get(), &xform.matrix);
569 
570 		return mask;
571 	}
572 } // anonymous namespace
573 
Blit(int x,int y,Bitmap const & src,Rect const & src_rect,Opacity const & opacity,Bitmap::BlendMode blend_mode)574 void Bitmap::Blit(int x, int y, Bitmap const& src, Rect const& src_rect, Opacity const& opacity, Bitmap::BlendMode blend_mode) {
575 	if (opacity.IsTransparent()) {
576 		return;
577 	}
578 
579 	auto mask = CreateMask(opacity, src_rect);
580 
581 	pixman_image_composite32(src.GetOperator(mask.get(), blend_mode),
582 							 src.bitmap.get(),
583 							 mask.get(), bitmap.get(),
584 							 src_rect.x, src_rect.y,
585 							 0, 0,
586 							 x, y,
587 							 src_rect.width, src_rect.height);
588 }
589 
BlitFast(int x,int y,Bitmap const & src,Rect const & src_rect,Opacity const & opacity)590 void Bitmap::BlitFast(int x, int y, Bitmap const & src, Rect const & src_rect, Opacity const & opacity) {
591 	if (opacity.IsTransparent()) {
592 		return;
593 	}
594 
595 	pixman_image_composite32(PIXMAN_OP_SRC,
596 		src.bitmap.get(),
597 		nullptr, bitmap.get(),
598 		src_rect.x, src_rect.y,
599 		0, 0,
600 		x, y,
601 		src_rect.width, src_rect.height);
602 }
603 
GetSubimage(Bitmap const & src,const Rect & src_rect)604 PixmanImagePtr Bitmap::GetSubimage(Bitmap const& src, const Rect& src_rect) {
605 	uint8_t* pixels = (uint8_t*) src.pixels() + src_rect.x * src.bpp() + src_rect.y * src.pitch();
606 	return PixmanImagePtr{ pixman_image_create_bits(src.pixman_format, src_rect.width, src_rect.height,
607 									(uint32_t*) pixels, src.pitch()) };
608 }
609 
TiledBlit(Rect const & src_rect,Bitmap const & src,Rect const & dst_rect,Opacity const & opacity,Bitmap::BlendMode blend_mode)610 void Bitmap::TiledBlit(Rect const& src_rect, Bitmap const& src, Rect const& dst_rect, Opacity const& opacity, Bitmap::BlendMode blend_mode) {
611 	TiledBlit(0, 0, src_rect, src, dst_rect, opacity, blend_mode);
612 }
613 
TiledBlit(int ox,int oy,Rect const & src_rect,Bitmap const & src,Rect const & dst_rect,Opacity const & opacity,Bitmap::BlendMode blend_mode)614 void Bitmap::TiledBlit(int ox, int oy, Rect const& src_rect, Bitmap const& src, Rect const& dst_rect, Opacity const& opacity, Bitmap::BlendMode blend_mode) {
615 	if (opacity.IsTransparent()) {
616 		return;
617 	}
618 
619 	if (ox >= src_rect.width)	ox %= src_rect.width;
620 	if (oy >= src_rect.height)	oy %= src_rect.height;
621 	if (ox < 0) ox += src_rect.width  * ((-ox + src_rect.width  - 1) / src_rect.width);
622 	if (oy < 0) oy += src_rect.height * ((-oy + src_rect.height - 1) / src_rect.height);
623 
624 	auto src_bm = GetSubimage(src, src_rect);
625 
626 	pixman_image_set_repeat(src_bm.get(), PIXMAN_REPEAT_NORMAL);
627 
628 	auto mask = CreateMask(opacity, src_rect);
629 
630 	pixman_image_composite32(src.GetOperator(mask.get(), blend_mode),
631 							 src_bm.get(), mask.get(), bitmap.get(),
632 							 ox, oy,
633 							 0, 0,
634 							 dst_rect.x, dst_rect.y,
635 							 dst_rect.width, dst_rect.height);
636 }
637 
StretchBlit(Bitmap const & src,Rect const & src_rect,Opacity const & opacity,Bitmap::BlendMode blend_mode)638 void Bitmap::StretchBlit(Bitmap const&  src, Rect const& src_rect, Opacity const& opacity, Bitmap::BlendMode blend_mode) {
639 	StretchBlit(GetRect(), src, src_rect, opacity, blend_mode);
640 }
641 
StretchBlit(Rect const & dst_rect,Bitmap const & src,Rect const & src_rect,Opacity const & opacity,Bitmap::BlendMode blend_mode)642 void Bitmap::StretchBlit(Rect const& dst_rect, Bitmap const& src, Rect const& src_rect, Opacity const& opacity, Bitmap::BlendMode blend_mode) {
643 	if (opacity.IsTransparent()) {
644 		return;
645 	}
646 
647 	double zoom_x = (double)src_rect.width  / dst_rect.width;
648 	double zoom_y = (double)src_rect.height / dst_rect.height;
649 
650 	Transform xform = Transform::Scale(zoom_x, zoom_y);
651 
652 	pixman_image_set_transform(src.bitmap.get(), &xform.matrix);
653 
654 	auto mask = CreateMask(opacity, src_rect, &xform);
655 
656 	pixman_image_composite32(src.GetOperator(mask.get(), blend_mode),
657 							 src.bitmap.get(), mask.get(), bitmap.get(),
658 							 src_rect.x / zoom_x, src_rect.y / zoom_y,
659 							 0, 0,
660 							 dst_rect.x, dst_rect.y,
661 							 dst_rect.width, dst_rect.height);
662 
663 	pixman_image_set_transform(src.bitmap.get(), nullptr);
664 }
665 
WaverBlit(int x,int y,double zoom_x,double zoom_y,Bitmap const & src,Rect const & src_rect,int depth,double phase,Opacity const & opacity,Bitmap::BlendMode blend_mode)666 void Bitmap::WaverBlit(int x, int y, double zoom_x, double zoom_y, Bitmap const& src, Rect const& src_rect, int depth, double phase, Opacity const& opacity, Bitmap::BlendMode blend_mode) {
667 	if (opacity.IsTransparent()) {
668 		return;
669 	}
670 
671 	Transform xform = Transform::Scale(1.0 / zoom_x, 1.0 / zoom_y);
672 
673 	pixman_image_set_transform(src.bitmap.get(), &xform.matrix);
674 
675 	auto mask = CreateMask(opacity, src_rect, &xform);
676 
677 	int height = static_cast<int>(std::floor(src_rect.height * zoom_y));
678 	int width  = static_cast<int>(std::floor(src_rect.width * zoom_x));
679 	const auto xoff = src_rect.x * zoom_x;
680 	const auto yoff = src_rect.y * zoom_y;
681 	const auto yclip = y < 0 ? -y : 0;
682 	const auto yend = std::min(height, this->height() - y);
683 	for (int i = yclip; i < yend; i++) {
684 		int dy = y + i;
685 		// RPG_RT starts the effect from the top of the screen even if the image is clipped. The result
686 		// is that moving images which cross the top of the screen can appear to go too fast or too slow
687 		// in RPT_RT. The (i - yclip) is RPG_RT compatible behavior. Just (i) would be more correct.
688 		const double sy = (i - yclip) * (2 * M_PI) / (32.0 * zoom_y);
689 		const int offset = 2 * zoom_x * depth * std::sin(phase + sy);
690 
691 		pixman_image_composite32(src.GetOperator(mask.get(), blend_mode),
692 								 src.bitmap.get(), mask.get(), bitmap.get(),
693 								 xoff, yoff + i,
694 								 0, i,
695 								 x + offset, dy,
696 								 width, 1);
697 	}
698 
699 	pixman_image_set_transform(src.bitmap.get(), nullptr);
700 }
701 
PixmanColor(const Color & color)702 static pixman_color_t PixmanColor(const Color &color) {
703 	pixman_color_t pcolor;
704 	pcolor.red = color.red * color.alpha;
705 	pcolor.green = color.green * color.alpha;
706 	pcolor.blue = color.blue * color.alpha;
707 	pcolor.alpha = color.alpha << 8;
708 	return pcolor;
709 }
710 
Fill(const Color & color)711 void Bitmap::Fill(const Color &color) {
712 	pixman_color_t pcolor = PixmanColor(color);
713 
714 	pixman_box32_t box = { 0, 0, width(), height() };
715 
716 	pixman_image_fill_boxes(PIXMAN_OP_SRC, bitmap.get(), &pcolor, 1, &box);
717 }
718 
FillRect(Rect const & dst_rect,const Color & color)719 void Bitmap::FillRect(Rect const& dst_rect, const Color &color) {
720 	pixman_color_t pcolor = PixmanColor(color);
721 
722 	auto timage = PixmanImagePtr{pixman_image_create_solid_fill(&pcolor)};
723 
724 	pixman_image_composite32(PIXMAN_OP_OVER,
725 			timage.get(), nullptr, bitmap.get(),
726 			0, 0,
727 			0, 0,
728 			dst_rect.x, dst_rect.y,
729 			dst_rect.width, dst_rect.height);
730 }
731 
Clear()732 void Bitmap::Clear() {
733 	if (!pixels()) {
734 		// Happens when height or width of bitmap are 0
735 		return;
736 	}
737 
738 	memset(pixels(), '\0', height() * pitch());
739 }
740 
ClearRect(Rect const & dst_rect)741 void Bitmap::ClearRect(Rect const& dst_rect) {
742 	pixman_color_t pcolor = {};
743 	pixman_box32_t box = {
744 		dst_rect.x,
745 		dst_rect.y,
746 		dst_rect.x + dst_rect.width,
747 		dst_rect.y + dst_rect.height
748 	};
749 
750 	pixman_image_fill_boxes(PIXMAN_OP_CLEAR, bitmap.get(), &pcolor, 1, &box);
751 }
752 
753 // Hard light lookup table mapping source color to destination color
754 // FIXME: Replace this with std::array<std::array<uint8_t,256>,256> when we have C++17
755 struct HardLightTable {
756 	uint8_t table[256][256] = {};
757 };
758 
make_hard_light_lookup()759 static constexpr HardLightTable make_hard_light_lookup() {
760 	HardLightTable hl;
761 	for (int i = 0; i < 256; ++i) {
762 		for (int j = 0; j < 256; ++j) {
763 			int res = 0;
764 			if (i <= 128)
765 				res = (2 * i * j) / 255;
766 			else
767 				res = 255 - 2 * (255 - i) * (255 - j) / 255;
768 			hl.table[i][j] = res > 255 ? 255 : res < 0 ? 0 : res;
769 		}
770 	}
771 	return hl;
772 }
773 
774 constexpr auto hard_light = make_hard_light_lookup();
775 
776 // Saturation Tone Inline: Changes a pixel saturation
saturation_tone(uint32_t & src_pixel,int saturation,int rs,int gs,int bs,int as)777 static inline void saturation_tone(uint32_t &src_pixel, int saturation, int rs, int gs, int bs, int as) {
778 	// Algorithm from OpenPDN (MIT license)
779 	// Transformation in Y'CbCr color space
780 	uint8_t r = (src_pixel >> rs) & 0xFF;
781 	uint8_t g = (src_pixel >> gs) & 0xFF;
782 	uint8_t b = (src_pixel >> bs) & 0xFF;
783 	uint8_t a = (src_pixel >> as) & 0xFF;
784 
785 	// Y' = 0.299 R' + 0.587 G' + 0.114 B'
786 	uint8_t lum = (7471 * b + 38470 * g + 19595 * r) >> 16;
787 
788 	// Scale Cb/Cr by scale factor "sat"
789 	int red = ((lum * 1024 + (r - lum) * saturation) >> 10);
790 	red = red > 255 ? 255 : red < 0 ? 0 : red;
791 	int green = ((lum * 1024 + (g - lum) * saturation) >> 10);
792 	green = green > 255 ? 255 : green < 0 ? 0 : green;
793 	int blue = ((lum * 1024 + (b - lum) * saturation) >> 10);
794 	blue = blue > 255 ? 255 : blue < 0 ? 0 : blue;
795 
796 	src_pixel = ((uint32_t)red << rs) | ((uint32_t)green << gs) | ((uint32_t)blue << bs) | ((uint32_t)a << as);
797 }
798 
799 // Color Tone Inline: Changes color of a pixel by hard light table
color_tone(uint32_t & src_pixel,Tone tone,int rs,int gs,int bs,int as)800 static inline void color_tone(uint32_t &src_pixel, Tone tone, int rs, int gs, int bs, int as) {
801 	src_pixel = ((uint32_t)hard_light.table[tone.red][(src_pixel >> rs) & 0xFF] << rs)
802 		| ((uint32_t)hard_light.table[tone.green][(src_pixel >> gs) & 0xFF] << gs)
803 		| ((uint32_t)hard_light.table[tone.blue][(src_pixel >> bs) & 0xFF] << bs)
804 		| ((uint32_t)((src_pixel >> as) & 0xFF) << as);
805 }
806 
ToneBlit(int x,int y,Bitmap const & src,Rect const & src_rect,const Tone & tone,Opacity const & opacity,bool check_alpha)807 void Bitmap::ToneBlit(int x, int y, Bitmap const& src, Rect const& src_rect, const Tone &tone, Opacity const& opacity, bool check_alpha) {
808 	if (opacity.IsTransparent()) {
809 		return;
810 	}
811 
812 	if (tone == Tone(128,128,128,128)) {
813 		if (&src != this) {
814 			Blit(x, y, src, src_rect, opacity);
815 		}
816 		return;
817 	}
818 
819 	// Only needed here, other codepaths are sanity checked by pixman
820 	if (x < 0 || y < 0 || x >= width() || y >= height()) {
821 		return;
822 	}
823 
824 	if (&src != this)
825 		pixman_image_composite32(src.GetOperator(),
826 		src.bitmap.get(), nullptr, bitmap.get(),
827 		src_rect.x, src_rect.y,
828 		0, 0,
829 		x, y,
830 		src_rect.width, src_rect.height);
831 
832 	int as = pixel_format.a.shift;
833 	int rs = pixel_format.r.shift;
834 	int gs = pixel_format.g.shift;
835 	int bs = pixel_format.b.shift;
836 	int next_row = pitch() / sizeof(uint32_t);
837 	uint32_t* pixels = (uint32_t*)this->pixels();
838 	pixels = pixels + (y - 1) * next_row + x;
839 
840 	uint16_t limit_height = std::min<uint16_t>(src_rect.height, height());
841 	uint16_t limit_width = std::min<uint16_t>(src_rect.width, width());
842 
843 	// If Saturation + Color:
844 	if (tone.gray != 128 && (tone.red != 128 || tone.green != 128 || tone.blue != 128)) {
845 		int sat = tone.gray > 128 ? 1024 + (tone.gray - 128) * 16 : tone.gray * 8;
846 
847 		if (&src != this || check_alpha) {
848 			for (uint16_t i = 0; i < limit_height; ++i) {
849 				pixels += next_row;
850 				for (uint16_t j = 0; j < limit_width; ++j) {
851 					if ((uint8_t)((pixels[j] >> as) & 0xFF) == 0)
852 						continue;
853 
854 					saturation_tone(pixels[j], sat, rs, gs, bs, as);
855 					color_tone(pixels[j], tone, rs, gs, bs, as);
856 				}
857 			}
858 		}
859 		else {
860 			for (uint16_t i = 0; i < limit_height; ++i) {
861 				pixels += next_row;
862 				for (uint16_t j = 0; j < limit_width; ++j) {
863 					saturation_tone(pixels[j], sat, rs, gs, bs, as);
864 					color_tone(pixels[j], tone, rs, gs, bs, as);
865 				}
866 			}
867 		}
868 	}
869 
870 	// If Only Saturation:
871 	else if (tone.gray != 128) {
872 		int sat = tone.gray > 128 ? 1024 + (tone.gray - 128) * 16 : tone.gray * 8;
873 
874 		if (&src != this || check_alpha) {
875 			for (uint16_t i = 0; i < limit_height; ++i) {
876 				pixels += next_row;
877 				for (uint16_t j = 0; j < limit_width; ++j) {
878 					if ((uint8_t)((pixels[j] >> as) & 0xFF) == 0)
879 						continue;
880 
881 					saturation_tone(pixels[j], sat, rs, gs, bs, as);
882 				}
883 			}
884 		}
885 		else {
886 			for (uint16_t i = 0; i < limit_height; ++i) {
887 				pixels += next_row;
888 				for (uint16_t j = 0; j < limit_width; ++j) {
889 					saturation_tone(pixels[j], sat, rs, gs, bs, as);
890 				}
891 			}
892 		}
893 	}
894 
895 	// If Only Color:
896 	else if (tone.red != 128 || tone.green != 128 || tone.blue != 128) {
897 		if (&src != this || check_alpha) {
898 			for (uint16_t i = 0; i < limit_height; ++i) {
899 				pixels += next_row;
900 				for (uint16_t j = 0; j < limit_width; ++j) {
901 					if ((uint8_t)((pixels[j] >> as) & 0xFF) == 0)
902 						continue;
903 
904 					color_tone(pixels[j], tone, rs, gs, bs, as);
905 				}
906 			}
907 		}
908 		else {
909 			for (uint16_t i = 0; i < limit_height; ++i) {
910 				pixels += next_row;
911 				for (uint16_t j = 0; j < limit_width; ++j) {
912 					color_tone(pixels[j], tone, rs, gs, bs, as);
913 				}
914 			}
915 		}
916 
917 	}
918 
919 }
920 
BlendBlit(int x,int y,Bitmap const & src,Rect const & src_rect,const Color & color,Opacity const & opacity)921 void Bitmap::BlendBlit(int x, int y, Bitmap const& src, Rect const& src_rect, const Color& color, Opacity const& opacity) {
922 	if (opacity.IsTransparent()) {
923 		return;
924 	}
925 
926 	if (color.alpha == 0) {
927 		if (&src != this)
928 			Blit(x, y, src, src_rect, opacity);
929 		return;
930 	}
931 
932 	if (&src != this)
933 		pixman_image_composite32(src.GetOperator(),
934 								 src.bitmap.get(), nullptr, bitmap.get(),
935 								 src_rect.x, src_rect.y,
936 								 0, 0,
937 								 x, y,
938 								 src_rect.width, src_rect.height);
939 
940 	pixman_color_t tcolor = PixmanColor(color);
941 	auto timage = PixmanImagePtr{ pixman_image_create_solid_fill(&tcolor) };
942 
943 	pixman_image_composite32(PIXMAN_OP_OVER,
944 							 timage.get(), src.bitmap.get(), bitmap.get(),
945 							 0, 0,
946 							 src_rect.x, src_rect.y,
947 							 x, y,
948 							 src_rect.width, src_rect.height);
949 }
950 
FlipBlit(int x,int y,Bitmap const & src,Rect const & src_rect,bool horizontal,bool vertical,Opacity const & opacity,Bitmap::BlendMode blend_mode)951 void Bitmap::FlipBlit(int x, int y, Bitmap const& src, Rect const& src_rect, bool horizontal, bool vertical, Opacity const& opacity, Bitmap::BlendMode blend_mode) {
952 	if (opacity.IsTransparent()) {
953 		return;
954 	}
955 
956 	bool has_xform = (horizontal || vertical);
957 	const auto img_w = src.GetWidth();
958 	const auto img_h = src.GetHeight();
959 
960 	auto rect = src_rect;
961 	if (has_xform) {
962 		Transform xform = Transform::Scale(horizontal ? -1 : 1, vertical ? -1 : 1);
963 		xform *= Transform::Translation(horizontal ? -img_w : 0, vertical ? -img_h : 0);
964 
965 		pixman_image_set_transform(src.bitmap.get(), &xform.matrix);
966 		const auto src_x = horizontal ? img_w - src_rect.x - src_rect.width : src_rect.x;
967 		const auto src_y = vertical ? img_h - src_rect.y - src_rect.height : src_rect.y;
968 
969 		rect = Rect{ src_x, src_y, src_rect.width, src_rect.height };
970 	}
971 
972 	Blit(x, y, src, rect, opacity, blend_mode);
973 
974 	if (has_xform) {
975 		pixman_image_set_transform(src.bitmap.get(), nullptr);
976 	}
977 }
978 
Flip(bool horizontal,bool vertical)979 void Bitmap::Flip(bool horizontal, bool vertical) {
980 	if (!horizontal && !vertical) {
981 		return;
982 	}
983 	const auto w = GetWidth();
984 	const auto h = GetHeight();
985 	const auto p = pitch();
986 
987 	auto temp = PixmanImagePtr{ pixman_image_create_bits(pixman_format, w, h, nullptr, p) };
988 
989 	std::memcpy(pixman_image_get_data(temp.get()),
990 			pixman_image_get_data(bitmap.get()),
991 			p * h);
992 
993 	Transform xform = Transform::Scale(horizontal ? -1 : 1, vertical ? -1 : 1);
994 	xform *= Transform::Translation(horizontal ? -w : 0, vertical ? -h : 0);
995 
996 	pixman_image_set_transform(temp.get(), &xform.matrix);
997 
998 	pixman_image_composite32(PIXMAN_OP_SRC,
999 							 temp.get(), nullptr, bitmap.get(),
1000 							 0, 0, 0, 0, 0, 0, w, h);
1001 }
1002 
MaskedBlit(Rect const & dst_rect,Bitmap const & mask,int mx,int my,Color const & color)1003 void Bitmap::MaskedBlit(Rect const& dst_rect, Bitmap const& mask, int mx, int my, Color const& color) {
1004 	pixman_color_t tcolor = {
1005 		static_cast<uint16_t>(color.red << 8),
1006 		static_cast<uint16_t>(color.green << 8),
1007 		static_cast<uint16_t>(color.blue << 8),
1008 		static_cast<uint16_t>(color.alpha << 8)};
1009 
1010 	auto source = PixmanImagePtr{ pixman_image_create_solid_fill(&tcolor) };
1011 
1012 	pixman_image_composite32(PIXMAN_OP_OVER,
1013 							 source.get(), mask.bitmap.get(), bitmap.get(),
1014 							 0, 0,
1015 							 mx, my,
1016 							 dst_rect.x, dst_rect.y,
1017 							 dst_rect.width, dst_rect.height);
1018 }
1019 
MaskedBlit(Rect const & dst_rect,Bitmap const & mask,int mx,int my,Bitmap const & src,int sx,int sy)1020 void Bitmap::MaskedBlit(Rect const& dst_rect, Bitmap const& mask, int mx, int my, Bitmap const& src, int sx, int sy) {
1021 	pixman_image_composite32(PIXMAN_OP_OVER,
1022 							 src.bitmap.get(), mask.bitmap.get(), bitmap.get(),
1023 							 sx, sy,
1024 							 mx, my,
1025 							 dst_rect.x, dst_rect.y,
1026 							 dst_rect.width, dst_rect.height);
1027 }
1028 
Blit2x(Rect const & dst_rect,Bitmap const & src,Rect const & src_rect)1029 void Bitmap::Blit2x(Rect const& dst_rect, Bitmap const& src, Rect const& src_rect) {
1030 	Transform xform = Transform::Scale(0.5, 0.5);
1031 
1032 	pixman_image_set_transform(src.bitmap.get(), &xform.matrix);
1033 
1034 	pixman_image_composite32(PIXMAN_OP_SRC,
1035 							 src.bitmap.get(), nullptr, bitmap.get(),
1036 							 src_rect.x, src_rect.y,
1037 							 0, 0,
1038 							 dst_rect.x, dst_rect.y,
1039 							 dst_rect.width, dst_rect.height);
1040 
1041 	pixman_image_set_transform(src.bitmap.get(), nullptr);
1042 }
1043 
EffectsBlit(int x,int y,int ox,int oy,Bitmap const & src,Rect const & src_rect,Opacity const & opacity,double zoom_x,double zoom_y,double angle,int waver_depth,double waver_phase,Bitmap::BlendMode blend_mode)1044 void Bitmap::EffectsBlit(int x, int y, int ox, int oy,
1045 						 Bitmap const& src, Rect const& src_rect,
1046 						 Opacity const& opacity,
1047 						 double zoom_x, double zoom_y, double angle,
1048 						 int waver_depth, double waver_phase, Bitmap::BlendMode blend_mode) {
1049 	if (opacity.IsTransparent()) {
1050 		return;
1051 	}
1052 
1053 	bool rotate = angle != 0.0;
1054 	bool scale = zoom_x != 1.0 || zoom_y != 1.0;
1055 	bool waver = waver_depth != 0;
1056 
1057 	if (waver) {
1058 		WaverBlit(x - ox * zoom_x, y - oy * zoom_y, zoom_x, zoom_y, src, src_rect,
1059 				  waver_depth, waver_phase, opacity, blend_mode);
1060 	}
1061 	else if (rotate) {
1062 		RotateZoomOpacityBlit(x, y, ox, oy, src, src_rect, angle, zoom_x, zoom_y, opacity, blend_mode);
1063 	}
1064 	else if (scale) {
1065 		ZoomOpacityBlit(x, y, ox, oy, src, src_rect, zoom_x, zoom_y, opacity, blend_mode);
1066 	}
1067 	else {
1068 		Blit(x - ox, y - oy, src, src_rect, opacity, blend_mode);
1069 	}
1070 }
1071 
RotateZoomOpacityBlit(int x,int y,int ox,int oy,Bitmap const & src,Rect const & src_rect,double angle,double zoom_x,double zoom_y,Opacity const & opacity,Bitmap::BlendMode blend_mode)1072 void Bitmap::RotateZoomOpacityBlit(int x, int y, int ox, int oy,
1073 		Bitmap const& src, Rect const& src_rect,
1074 		double angle, double zoom_x, double zoom_y, Opacity const& opacity, Bitmap::BlendMode blend_mode)
1075 {
1076 	if (opacity.IsTransparent()) {
1077 		return;
1078 	}
1079 
1080 	auto* src_img = src.bitmap.get();
1081 
1082 	Transform fwd = Transform::Translation(x, y);
1083 	fwd *= Transform::Rotation(angle);
1084 	if (zoom_x != 1.0 || zoom_y != 1.0) {
1085 		fwd *= Transform::Scale(zoom_x, zoom_y);
1086 	}
1087 	fwd *= Transform::Translation(-ox, -oy);
1088 
1089 	Rect dst_rect = TransformRectangle(fwd, Rect{0, 0, src_rect.width, src_rect.height});
1090 	dst_rect.Adjust(GetRect());
1091 	if (dst_rect.IsEmpty())
1092 		return;
1093 
1094 	auto inv = fwd.Inverse();
1095 
1096 	PixmanImagePtr temp;
1097 	if (src_rect != src.GetRect()) {
1098 		temp = GetSubimage(src, src_rect);
1099 		src_img = temp.get();
1100 	}
1101 
1102 	pixman_image_set_transform(src_img, &inv.matrix);
1103 
1104 	auto mask = CreateMask(opacity, src_rect, &inv);
1105 
1106 	pixman_image_composite32(GetOperator(mask.get(), blend_mode),
1107 							 src_img, mask.get(), bitmap.get(),
1108 							 dst_rect.x, dst_rect.y,
1109 							 dst_rect.x, dst_rect.y,
1110 							 dst_rect.x, dst_rect.y,
1111 							 dst_rect.width, dst_rect.height);
1112 
1113 	pixman_image_set_transform(src_img, nullptr);
1114 }
1115 
ZoomOpacityBlit(int x,int y,int ox,int oy,Bitmap const & src,Rect const & src_rect,double zoom_x,double zoom_y,Opacity const & opacity,Bitmap::BlendMode blend_mode)1116 void Bitmap::ZoomOpacityBlit(int x, int y, int ox, int oy,
1117 							 Bitmap const& src, Rect const& src_rect,
1118 							 double zoom_x, double zoom_y,
1119 							 Opacity const& opacity, Bitmap::BlendMode blend_mode)
1120 {
1121 	if (opacity.IsTransparent()) {
1122 		return;
1123 	}
1124 
1125 	Rect dst_rect(
1126 		x - static_cast<int>(std::floor(ox * zoom_x)),
1127 		y - static_cast<int>(std::floor(oy * zoom_y)),
1128 		static_cast<int>(std::floor(src_rect.width * zoom_x)),
1129 		static_cast<int>(std::floor(src_rect.height * zoom_y)));
1130 	StretchBlit(dst_rect, src, src_rect, opacity, blend_mode);
1131 }
1132 
GetOperator(pixman_image_t * mask,Bitmap::BlendMode blend_mode) const1133 pixman_op_t Bitmap::GetOperator(pixman_image_t* mask, Bitmap::BlendMode blend_mode) const {
1134 	if (blend_mode != BlendMode::Default) {
1135 		switch (blend_mode) {
1136 			case BlendMode::Normal:
1137 				return PIXMAN_OP_OVER;
1138 			case BlendMode::NormalWithoutAlpha:
1139 				return PIXMAN_OP_SRC;
1140 			case BlendMode::XOR:
1141 				return PIXMAN_OP_XOR;
1142 			case BlendMode::Additive:
1143 				return PIXMAN_OP_ADD;
1144 			case BlendMode::Multiply:
1145 				return PIXMAN_OP_MULTIPLY;
1146 			case BlendMode::Overlay:
1147 				return PIXMAN_OP_OVERLAY;
1148 			case BlendMode::Saturate:
1149 				return PIXMAN_OP_SATURATE;
1150 			case BlendMode::Darken:
1151 				return PIXMAN_OP_DARKEN;
1152 			case BlendMode::Lighten:
1153 				return PIXMAN_OP_LIGHTEN;
1154 			case BlendMode::ColorDodge:
1155 				return PIXMAN_OP_COLOR_DODGE;
1156 			case BlendMode::ColorBurn:
1157 				return PIXMAN_OP_COLOR_BURN;
1158 			case BlendMode::Difference:
1159 				return PIXMAN_OP_DIFFERENCE;
1160 			case BlendMode::Exclusion:
1161 				return PIXMAN_OP_EXCLUSION;
1162 			case BlendMode::SoftLight:
1163 				return PIXMAN_OP_SOFT_LIGHT;
1164 			case BlendMode::HardLight:
1165 				return PIXMAN_OP_HARD_LIGHT;
1166 			default:
1167 				return PIXMAN_OP_CLEAR;
1168 		}
1169 	}
1170 
1171 	if (!mask && (!GetTransparent() || GetImageOpacity() == ImageOpacity::Opaque)) {
1172 		return PIXMAN_OP_SRC;
1173 	}
1174 
1175 	return PIXMAN_OP_OVER;
1176 }
1177 
EdgeMirrorBlit(int x,int y,Bitmap const & src,Rect const & src_rect,bool mirror_x,bool mirror_y,Opacity const & opacity)1178 void Bitmap::EdgeMirrorBlit(int x, int y, Bitmap const& src, Rect const& src_rect, bool mirror_x, bool mirror_y, Opacity const& opacity) {
1179 	if (opacity.IsTransparent())
1180 		return;
1181 
1182 	auto mask = CreateMask(opacity, src_rect);
1183 
1184 	const auto dst_rect = GetRect();
1185 
1186 	auto draw = [&](int x, int y) {
1187 		pixman_image_composite32(src.GetOperator(mask.get()),
1188 				src.bitmap.get(),
1189 				mask.get(), bitmap.get(),
1190 				src_rect.x, src_rect.y,
1191 				0, 0,
1192 				x, y,
1193 				src_rect.width, src_rect.height);
1194 	};
1195 
1196 	draw(x, y);
1197 
1198 	const bool clone_x = (mirror_x && x + src_rect.width > dst_rect.width);
1199 	const bool clone_y = (mirror_y && y + src_rect.height > dst_rect.height);
1200 
1201 	if (clone_x) {
1202 		draw(x - dst_rect.width, y);
1203 	}
1204 
1205 	if (clone_y) {
1206 		draw(x, y - dst_rect.height);
1207 	}
1208 
1209 	if (clone_x && clone_y) {
1210 		draw(x - dst_rect.width, y - dst_rect.height);
1211 	}
1212 }
1213 
1214