Fix integer overflow in ft_rendered_size_line
[ilaris-y4m-tools.git] / rendertext.cpp
blobe560430fb939ae7e6778226edacb226dc4750ed2
1 #include "rendertext.hpp"
2 #include "parseval.hpp"
3 #include "yuv4mpeg.hpp"
4 #include "yuvconvert.hpp"
5 #include <fstream>
6 #include <iostream>
7 #include <ft2build.h>
8 #include FT_FREETYPE_H
10 namespace
12 static bool ft_init;
13 static FT_Library ft_handle;
15 void mix(uint16_t& a, uint16_t b, uint32_t t)
17 a = (a * (65536 - t) + b * t) >> 16;
20 void mix(uint8_t& a, uint8_t b, uint32_t t)
22 a = (a * (256 - t) + b * t) >> 8;
25 void render_halo(uint8_t* dest, const uint8_t* src, size_t width, size_t height, signed thickness)
27 if(dest != src)
28 memcpy(dest, src, width * height);
29 for(unsigned j = 0; j < height; j++)
30 for(unsigned i = 0; i < width; i++)
31 if(dest[j * width + i] == 1)
32 for(signed y = -thickness; y <= thickness; y++) {
33 if(j + y < 0)
34 continue;
35 if(j + y >= height)
36 break;
37 for(signed x = -thickness; x <= thickness; x++) {
38 if(i + x < 0)
39 continue;
40 if(i + x >= width)
41 break;
42 if(dest[(j + y) * width + (i + x)] == 0)
43 dest[(j + y) * width + (i + x)] = 2;
48 struct errmsg
50 int code;
51 const char* message;
54 void throw_ft_error(FT_Error code)
56 if(!code)
57 return;
58 //There's no built-in error table in Freetype 2???
59 #undef __FTERRORS_H__
60 #define FT_ERRORDEF(e, v, s) {e, s},
61 #define FT_ERROR_START_LIST {
62 #define FT_ERROR_END_LIST {0, 0}};
63 static struct errmsg freetype_errors[] =
64 #include FT_ERRORS_H
65 struct errmsg* i = freetype_errors;
66 while(i->message) {
67 if(i->code == code)
68 throw std::runtime_error(std::string("Freetype2 error: ") + i->message);
69 i++;
71 std::ostringstream s;
72 s << "Freetype2 error: Unknown error #" << code;
73 throw std::runtime_error(s.str());
76 struct render_line_info
78 size_t width;
79 size_t height;
80 size_t baseline;
81 size_t offset;
84 struct render_line_info ft_rendered_size_line(FT_Face face, const std::string& text)
86 struct render_line_info l;
87 uint16_t state = utf8_initial_state;
88 size_t len = text.length();
89 size_t basepos = 0;
90 l.width = l.height = l.baseline = l.offset = 0;
91 for(size_t i = 0; i <= len; i++) {
92 ssize_t base;
93 int32_t sym = utf8_parse_byte((i < len) ? static_cast<uint8_t>(text[i]) : -1, state);
94 if(sym < 0)
95 continue;
96 //Got unicode symbol.
97 unsigned gslot = FT_Get_Char_Index(face, sym);
98 if(!gslot) {
99 //Missing symbol!
100 std::cerr << "Warning: Symbol " << sym << " not present in font!" << std::endl;
101 continue;
103 try {
104 throw_ft_error(FT_Load_Glyph(face, gslot, FT_LOAD_DEFAULT));
105 throw_ft_error(FT_Render_Glyph(face->glyph, FT_RENDER_MODE_MONO));
106 } catch(std::exception& e) {
107 std::cerr << "Warning: Symbol " << sym << " can't be rendered: " << e.what()
108 << std::endl;
109 continue;
111 //If offset needs adjustment, do that.
112 base = basepos + face->glyph->bitmap_left;
113 if(base < 0 && l.offset < static_cast<size_t>(-base))
114 l.offset = static_cast<size_t>(-base);
115 //If baseline need adjustment, do that.
116 base = -face->glyph->bitmap_top;
117 if(base < 0 && l.baseline < static_cast<size_t>(-base))
118 l.baseline = static_cast<size_t>(-base);
119 //Compute width.
120 base = basepos + face->glyph->bitmap_left + (ssize_t)face->glyph->bitmap.width;
121 if(base > static_cast<ssize_t>(l.width))
122 l.width = base;
123 //Compute height.
124 base = -face->glyph->bitmap_top + (ssize_t)face->glyph->bitmap.rows;
125 if(base > static_cast<ssize_t>(l.height))
126 l.height = base;
127 basepos = basepos + (face->glyph->advance.x >> 6);
129 //Width / height don't take negative excursions into account.
130 l.width = l.width + l.offset;
131 l.height = l.height + l.baseline;
132 if(l.height > 1000000000 || l.width > 1000000000) {
133 std::cerr << "Subtitle: Internal Error: Insane row size!" << std::endl;
134 abort();
136 return l;
139 ssize_t align_position(size_t space, size_t size, int32_t pos)
141 ssize_t freespace = space - size;
142 return ((pos + 1000) * freespace) / 2000;
145 void ft_render_text_line(FT_Face face, const std::string& text, std::vector<uint8_t>& data, size_t width,
146 size_t height, ssize_t alignment, size_t y, struct render_line_info l)
148 ssize_t p = align_position(width, l.width, alignment);
149 if(y >= height || p >= static_cast<ssize_t>(width))
150 return; //Nothing fits.
151 ssize_t offset_x;
152 ssize_t offset_y;
153 ssize_t basepos = 0;
154 uint16_t state = utf8_initial_state;
155 size_t len = text.length();
156 //We need to compute the window offset.
157 offset_y = l.baseline + y;
158 offset_x = l.offset + p;
159 for(size_t i = 0; i <= len; i++) {
160 int32_t sym = utf8_parse_byte((i < len) ? static_cast<uint8_t>(text[i]) : -1, state);
161 if(sym < 0)
162 continue;
163 //Got unicode symbol.
164 unsigned gslot = FT_Get_Char_Index(face, sym);
165 if(!gslot) {
166 //Missing symbol!
167 std::cerr << "Warning: Symbol " << sym << " not present in font!" << std::endl;
168 continue;
170 try {
171 throw_ft_error(FT_Load_Glyph(face, gslot, FT_LOAD_DEFAULT));
172 throw_ft_error(FT_Render_Glyph(face->glyph, FT_RENDER_MODE_MONO));
173 } catch(std::exception& e) {
174 std::cerr << "Warning: Symbol " << sym << " can't be rendered: " << e.what()
175 << std::endl;
176 continue;
178 ssize_t render_x = basepos + face->glyph->bitmap_left + offset_x;
179 ssize_t render_y = -face->glyph->bitmap_top + offset_y;
180 for(int j = 0; (unsigned)j < face->glyph->bitmap.rows && render_y + j <
181 static_cast<ssize_t>(height); j++) {
182 if(render_y + j < 0)
183 continue;
184 const uint8_t* ldata = face->glyph->bitmap.buffer + j * face->glyph->bitmap.pitch;
185 uint8_t* tmpr = &data[(render_y + j) * width];
186 for(int k = 0; (unsigned)k < face->glyph->bitmap.width && render_x + k <
187 static_cast<ssize_t>(width); k++) {
188 if(render_x + k < 0)
189 continue;
190 tmpr[render_x + k] = ((ldata[k >> 3] >> (7 - (k & 7))) & 1) ? 1 : 0;
193 basepos = basepos + (face->glyph->advance.x >> 6);
197 void ft_render_text(FT_Face face, const std::string& text, std::vector<uint8_t>& data, size_t width,
198 size_t height, ssize_t alignment, size_t spacing, size_t y = 0)
200 std::string _text = text;
201 //If the string contains linefeeds, break it up.
202 size_t p = _text.find("\\n");
203 if(p < _text.length()) {
204 std::string t1 = _text.substr(0, p);
205 std::string t2 = _text.substr(p + 2);
206 auto g1 = ft_rendered_size_line(face, t1);
207 ft_render_text_line(face, t1, data, width, height, alignment, y, g1);
208 ft_render_text(face, t2, data, width, height, alignment, spacing, y + g1.height + spacing);
209 return;
211 //The text is in one piece if this place is reached.
212 auto g1 = ft_rendered_size_line(face, text);
213 ft_render_text_line(face, text, data, width, height, alignment, y, g1);
216 std::pair<size_t, size_t> ft_rendered_size(FT_Face face, const std::string& text, size_t spacing)
218 std::string _text = text;
219 //If the string contains linefeeds, break it up.
220 size_t p = _text.find("\\n");
221 if(p < _text.length()) {
222 std::string t1 = _text.substr(0, p);
223 std::string t2 = _text.substr(p + 2);
224 auto g1 = ft_rendered_size_line(face, t1);
225 auto g2 = ft_rendered_size(face, t2, spacing);
226 size_t w, h;
227 w = (g1.width > g2.first) ? g1.width : g2.first;
228 h = g1.height + spacing + g2.second;
229 return std::make_pair(w, h);
231 //The text is in one piece if this place is reached.
232 auto g = ft_rendered_size_line(face, text);
233 return std::make_pair(g.width, g.height);
236 std::string read_subtitle_from_file(std::string filename)
238 std::string content;
239 std::string tmp;
240 std::ifstream x(filename);
241 if(!x)
242 throw std::runtime_error("Can't open '" + filename + "'");
243 while(std::getline(x, tmp)) {
244 if(content != "")
245 content = content + "\n" + tmp;
246 else
247 content = tmp;
249 std::ostringstream content2;
250 for(size_t i = 0; i < content.length(); i++)
251 if(content[i] == '\n')
252 content2 << "\\n";
253 else if(content[i] == '\\')
254 content2 << "\\\\";
255 else
256 content2 << content[i];
257 return content2.str();
260 ssize_t position_subtitle(size_t space, size_t size, int32_t pos, bool absolute)
262 if(absolute)
263 return pos;
264 else {
265 ssize_t freespace = space - size;
266 return ((pos + 1000) * freespace) / 2000;
270 yuvc_converter* get_converter(const regex_results& r)
272 std::string csp = "rec601";
273 if(r)
274 csp = r[1];
275 yuvc_converter* c = yuvc_get_converter(RGB_RGB, csp);
276 if(c)
277 return c;
278 if(r)
279 std::cerr << "Warning: Stream YUV variant unspecified, assuming rec601." << std::endl;
280 else
281 std::cerr << "Warning: Stream YUV variant unsupported, falling back to rec601." << std::endl;
282 return yuvc_get_converter(RGB_RGB, "rec601");
285 void stamp_pos(size_t space, size_t dimension, ssize_t pos, size_t& fpos, size_t& spos, size_t& sdim)
287 sdim = dimension;
288 if(pos < 0) {
289 spos = -pos;
290 if(sdim <= spos) {
291 fpos = 0;
292 spos = 0;
293 sdim = 0;
294 return;
296 sdim = sdim + pos;
297 fpos = 0;
298 } else {
299 spos = 0;
300 fpos = pos;
302 if(fpos > space)
303 sdim = 0;
304 else if(fpos + sdim > space)
305 sdim = space - fpos;
309 std::pair<size_t, size_t> text_render_parameters::render_size(const std::string& text)
311 FT_Face face;
313 //Init freetype2 if needed.
314 if(!ft_init)
315 throw_ft_error(FT_Init_FreeType(&ft_handle));
316 ft_init = true;
318 //Load the font face.
319 if(fontfile == "")
320 throw std::runtime_error("Font file needed for text rendering");
321 throw_ft_error(FT_New_Face(ft_handle, fontfile.c_str(), fontface, &face));
322 throw_ft_error(FT_Set_Pixel_Sizes(face, 0, fontsize));
324 auto g = ft_rendered_size(face, text, text_spacing);
325 return std::make_pair(g.first + 2 * halo_thickness, g.second + 2 * halo_thickness);
328 parsed_png& text_render_parameters::render(const std::string& text)
330 FT_Face face;
331 std::vector<uint8_t> tmp;
332 size_t width, height;
333 size_t width2, height2;
335 //Init freetype2 if needed.
336 if(!ft_init)
337 throw_ft_error(FT_Init_FreeType(&ft_handle));
338 ft_init = true;
340 //Load the font face.
341 if(fontfile == "")
342 throw std::runtime_error("Font file needed for text rendering");
343 throw_ft_error(FT_New_Face(ft_handle, fontfile.c_str(), fontface, &face));
344 throw_ft_error(FT_Set_Pixel_Sizes(face, 0, fontsize));
346 //First we need to figure out the size of the rendered bitmap. Then render the actual bitmap.
347 auto g = ft_rendered_size(face, text, text_spacing);
348 width = g.first;
349 height = g.second;
350 width2 = width + 2 * halo_thickness;
351 height2 = height + 2 * halo_thickness;
352 tmp.resize(width2 * height2);
353 memset(&tmp[0], 0, width2 * height2);
354 ft_render_text(face, text, tmp, width, height, text_alignment, text_spacing);
356 //Do in-place padding by halo_thickness on all sides.
357 if(halo_thickness > 0) {
358 for(size_t i = height - 1; i < height; i--)
359 memmove(&tmp[width2 * (i + halo_thickness) + halo_thickness], &tmp[width * i], width);
360 for(size_t i = 0; i < height; i++) {
361 memset(&tmp[width2 * (i + halo_thickness)], 0, halo_thickness);
362 memset(&tmp[width2 * (i + halo_thickness + 1) - halo_thickness], 0, halo_thickness);
364 memset(&tmp[0], 0, width2 * halo_thickness);
365 render_halo(&tmp[0], &tmp[0], width2, height2, halo_thickness);
368 parsed_png& png = *new parsed_png(width2, height2);
369 for(size_t i = 0; i < width2 * height2; i++) {
370 if(tmp[i] == 0) png.data[i] = bg_color;
371 if(tmp[i] == 1) png.data[i] = fg_color;
372 if(tmp[i] == 2) png.data[i] = halo_color;
374 return png;
377 text_render_parameters::text_render_parameters()
379 fontsize = 12;
380 text_alignment = 0;
381 text_spacing = 0;
382 fontface = 0;
383 fg_color = 0xFFFFFFFFU;
384 halo_color = 0xFF000000U;
385 bg_color = 0;
386 halo_thickness = 1;
389 bool text_render_parameters::argument(const std::string& arg)
391 regex_results r;
392 if(r = regex("--font-file=(.*)", arg)) {
393 fontfile = r[1];
394 return true;
395 } else if(r = regex("--font-size=(.*)", arg)) {
396 fontsize = parse_value<unsigned>(r[1]);
397 return true;
398 } else if(r = regex("--text-alignment=left", arg)) {
399 text_alignment = -1000;
400 return true;
401 } else if(r = regex("--text-alignment=center", arg)) {
402 text_alignment = 0;
403 return true;
404 } else if(r = regex("--text-alignment=right", arg)) {
405 text_alignment = 1000;
406 return true;
407 } else if(r = regex("--text-alignment=(.*)", arg)) {
408 text_alignment = parse_value<signed>(r[1]);
409 return true;
410 } else if(r = regex("--text-spacing=(.*)", arg)) {
411 text_spacing = parse_value<signed>(r[1]);
412 return true;
413 } else if(r = regex("--font-face=(.*)", arg)) {
414 fontface = parse_value<long>(r[1]);
415 return true;
416 } else if(r = regex("--fg-color=(.*),(.*),(.*),(.*)", arg)) {
417 unsigned R = parse_value<unsigned>(r[1]);
418 unsigned g = parse_value<unsigned>(r[2]);
419 unsigned b = parse_value<unsigned>(r[3]);
420 unsigned a = parse_value<unsigned>(r[4]);
421 fg_color = (a << 24) | (b << 16) | (g << 8) | R;
422 return true;
423 } else if(r = regex("--fg-color=(.*),(.*),(.*)", arg)) {
424 unsigned R = parse_value<unsigned>(r[1]);
425 unsigned g = parse_value<unsigned>(r[2]);
426 unsigned b = parse_value<unsigned>(r[3]);
427 fg_color = (fg_color & 0xFF000000U) | (b << 16) | (g << 8) | R;
428 return true;
429 } else if(r = regex("--fg-alpha=(.*)", arg)) {
430 unsigned a = parse_value<unsigned>(r[1]);
431 fg_color = (fg_color & 0xFFFFFFU) | (a << 24);
432 return true;
433 } else if(r = regex("--bg-color=(.*),(.*),(.*),(.*)", arg)) {
434 unsigned R = parse_value<unsigned>(r[1]);
435 unsigned g = parse_value<unsigned>(r[2]);
436 unsigned b = parse_value<unsigned>(r[3]);
437 unsigned a = parse_value<unsigned>(r[4]);
438 bg_color = (a << 24) | (b << 16) | (g << 8) | R;
439 return true;
440 } else if(r = regex("--bg-color=(.*),(.*),(.*)", arg)) {
441 unsigned R = parse_value<unsigned>(r[1]);
442 unsigned g = parse_value<unsigned>(r[2]);
443 unsigned b = parse_value<unsigned>(r[3]);
444 bg_color = (bg_color & 0xFF000000U) | (b << 16) | (g << 8) | R;
445 return true;
446 } else if(r = regex("--bg-alpha=(.*)", arg)) {
447 unsigned a = parse_value<unsigned>(r[1]);
448 bg_color = (bg_color & 0xFFFFFFU) | (a << 24);
449 return true;
450 } else if(r = regex("--halo-color=(.*),(.*),(.*),(.*)", arg)) {
451 unsigned R = parse_value<unsigned>(r[1]);
452 unsigned g = parse_value<unsigned>(r[2]);
453 unsigned b = parse_value<unsigned>(r[3]);
454 unsigned a = parse_value<unsigned>(r[4]);
455 halo_color = (a << 24) | (b << 16) | (g << 8) | R;
456 return true;
457 } else if(r = regex("--halo-color=(.*),(.*),(.*)", arg)) {
458 unsigned R = parse_value<unsigned>(r[1]);
459 unsigned g = parse_value<unsigned>(r[2]);
460 unsigned b = parse_value<unsigned>(r[3]);
461 halo_color = (halo_color & 0xFF000000U) | (b << 16) | (g << 8) | R;
462 return true;
463 } else if(r = regex("--halo-alpha=(.*)", arg)) {
464 unsigned a = parse_value<unsigned>(r[1]);
465 halo_color = (halo_color & 0xFFFFFFU) | (a << 24);
466 return true;
467 } else if(r = regex("--halo-thickness=(.*)", arg)) {
468 halo_thickness = parse_value<unsigned>(r[1]);
469 if(halo_thickness < 0)
470 halo_thickness = 0;
471 return true;
472 } else
473 return false;
476 pre_subtitle::pre_subtitle()
478 image = NULL;
481 pre_subtitle::~pre_subtitle()
483 delete[] image;
486 subtitle::subtitle(const pre_subtitle& presub, const yuv4mpeg_stream_header& strmh)
488 double fps = 25;
489 if(strmh.fps_n && strmh.fps_d)
490 fps = 1.0 * strmh.fps_n / strmh.fps_d;
491 frame = presub.start.get_frame(fps, 0);
492 duration = presub.duration.get_frame(fps, 0);
493 width = presub.image->width;
494 height = presub.image->height;
495 x = position_subtitle(strmh.width, width, presub.xpos, presub.xabsolute);
496 y = position_subtitle(strmh.height, height, presub.ypos, presub.yabsolute);
497 if(x < 0 || y < 0 || x + width > strmh.width || y + height > strmh.height)
498 std::cerr << "Warning: Subtitle is partially offscreen" << std::endl;
500 if(strmh.chroma == "rgb") {
501 bits16 = false;
502 data = new uint8_t[width * height * 3];
503 planes = 1;
504 planesep = width * height * 3;
505 for(size_t i = 0; i < width * height; i++) {
506 data[3 * i + 0] = presub.image->data[i];
507 data[3 * i + 1] = presub.image->data[i] >> 8;
508 data[3 * i + 2] = presub.image->data[i] >> 16;
510 } else if(strmh.chroma == "444" || strmh.chroma == "444p16") {
511 std::vector<uint8_t> tmp;
512 bits16 = (strmh.chroma == "444p16");
513 tmp.resize(width * height * 3);
514 data = new uint8_t[width * height * (bits16 ? 6 : 3)];
515 uint16_t* data16 = reinterpret_cast<uint16_t*>(data);
516 planes = 3;
517 planesep = width * height;
518 yuvc_converter* conv = get_converter(strmh.find_extension("yuvmatrix=(.*)"));
519 for(size_t i = 0; i < width * height; i++) {
520 tmp[3 * i + 0] = presub.image->data[i];
521 tmp[3 * i + 1] = presub.image->data[i] >> 8;
522 tmp[3 * i + 2] = presub.image->data[i] >> 16;
524 if(bits16)
525 conv->transform(data16, &tmp[0], width * height);
526 else
527 conv->transform(data, &tmp[0], width * height);
528 } else
529 (stringfmt() << "Chroma type " << strmh.chroma << " not supported for subtitling").throwex();
530 alpha = new uint32_t[width * height];
531 if(bits16)
532 for(size_t i = 0; i < width * height; i++)
533 alpha[i] = ((presub.image->data[i] >> 24) + (presub.image->data[i] >> 31)) << 8;
534 else
535 for(size_t i = 0; i < width * height; i++)
536 alpha[i] = (presub.image->data[i] >> 24) + (presub.image->data[i] >> 31);
539 subtitle::~subtitle()
541 delete[] data;
542 delete[] alpha;
546 subtitle_render_context::subtitle_render_context()
548 xpos = 0;
549 ypos = -1000;
550 xabsolute = false;
551 yabsolute = false;
552 duration = timemarker("5.0");
555 bool subtitle_render_context::argument(const std::string& arg, pre_subtitle*& presub)
557 regex_results r;
558 if(tparams.argument(arg))
559 return true;
560 if(r = regex("--(text|file|png)=([^,]*),(.*)", arg)) {
561 std::string text = r[3];
562 timemarker start(r[2]);
563 parsed_png* png;
564 if(r[1] == "file")
565 text = read_subtitle_from_file(text);
566 if(r[1] == "png")
567 png = new parsed_png(text);
568 else
569 png = &tparams.render(text);
570 presub = new pre_subtitle;
571 presub->image = png;
572 presub->xpos = xpos;
573 presub->ypos = ypos;
574 presub->xabsolute = xabsolute;
575 presub->yabsolute = yabsolute;
576 presub->start = start;
577 presub->duration = duration;
578 return true;
579 } else if(r = regex("--duration=(.*)", arg)) {
580 duration = timemarker(r[1]);
581 return true;
582 } else if(r = regex("--xpos=left", arg)) {
583 xabsolute = false;
584 xpos = -1000;
585 return true;
586 } else if(r = regex("--xpos=center", arg)) {
587 xabsolute = false;
588 xpos = 0;
589 return true;
590 } else if(r = regex("--xpos=right", arg)) {
591 xabsolute = false;
592 xpos = 1000;
593 return true;
594 } else if(r = regex("--xpos=abs:([+-]?[0-9]+)", arg)) {
595 xabsolute = true;
596 xpos = parse_value<ssize_t>(r[1]);
597 return true;
598 } else if(r = regex("--xpos=([+-]?[0-9]+)", arg)) {
599 xabsolute = false;
600 xpos = parse_value<ssize_t>(r[1]);
601 return true;
602 } else if(r = regex("--ypos=top", arg)) {
603 yabsolute = false;
604 ypos = -1000;
605 return true;
606 } else if(r = regex("--ypos=center", arg)) {
607 yabsolute = false;
608 ypos = 0;
609 return true;
610 } else if(r = regex("--ypos=bottom", arg)) {
611 yabsolute = false;
612 ypos = 1000;
613 return true;
614 } else if(r = regex("--ypos=abs:([+-]?[0-9]+)", arg)) {
615 yabsolute = true;
616 ypos = parse_value<ssize_t>(r[1]);
617 return true;
618 } else if(r = regex("--ypos=([+-]?[0-9]+)", arg)) {
619 yabsolute = false;
620 ypos = parse_value<ssize_t>(r[1]);
621 return true;
622 } else {
623 return false;
627 std::vector<subtitle*> subtitle::from_presub(const std::vector<pre_subtitle*>& presubs,
628 const yuv4mpeg_stream_header& strmh)
630 std::vector<subtitle*> ret;
631 for(auto i : presubs) {
632 try {
633 ret.push_back(new subtitle(*i, strmh));
634 } catch(...) {
635 for(auto j : ret)
636 delete j;
639 return ret;
642 void subtitle::stamp(uint8_t* idata, size_t iwidth, size_t iheight, uint64_t frameno) const
644 if(frameno < frame || frameno > frame + duration)
645 return;
646 size_t fpos_x, spos_x, swidth;
647 size_t fpos_y, spos_y, sheight;
648 stamp_pos(iwidth, width, x, fpos_x, spos_x, swidth);
649 stamp_pos(iheight, height, y, fpos_y, spos_y, sheight);
650 if(!swidth || !sheight)
651 return;
652 if(planes == 3 && bits16) {
653 size_t iplanesep = iwidth * iheight;
654 uint16_t* idata2 = reinterpret_cast<uint16_t*>(idata);
655 const uint16_t* data2 = reinterpret_cast<const uint16_t*>(data);
656 for(size_t i = 0; i < sheight; i++)
657 for(size_t j = 0; j < swidth; j++) {
658 size_t ioffset = (fpos_y + i) * iwidth + (fpos_x + j);
659 size_t offset = (spos_y + i) * width + (spos_x + j);
660 mix(idata2[ioffset], data2[offset], alpha[offset]);
661 mix(idata2[ioffset + iplanesep], data2[offset + planesep], alpha[offset]);
662 mix(idata2[ioffset + iplanesep], data2[offset + planesep], alpha[offset]);
664 } else if(planes == 3 && !bits16) {
665 size_t iplanesep = iwidth * iheight;
666 for(size_t i = 0; i < sheight; i++)
667 for(size_t j = 0; j < swidth; j++) {
668 size_t ioffset = (fpos_y + i) * iwidth + (fpos_x + j);
669 size_t offset = (spos_y + i) * width + (spos_x + j);
670 mix(idata[ioffset], data[offset], alpha[offset]);
671 mix(idata[ioffset + iplanesep], data[offset + planesep], alpha[offset]);
672 mix(idata[ioffset + 2 * iplanesep], data[offset + 2 * planesep], alpha[offset]);
674 } else if(planes == 1) {
675 for(size_t i = 0; i < sheight; i++)
676 for(size_t j = 0; j < swidth; j++) {
677 size_t ioffset = (fpos_y + i) * 3 * iwidth + 3 * (fpos_x + j);
678 size_t offset2 = (spos_y + i) * width + (spos_x + j);
679 size_t offset = offset2 * 3;
680 mix(idata[ioffset], data[offset], alpha[offset2]);
681 mix(idata[ioffset + 1], data[offset + 1], alpha[offset2]);
682 mix(idata[ioffset + 2], data[offset + 2], alpha[offset2]);