301{
303
304 if (filenames.empty() || states.empty() || sprite_size_px == 0)
305 return false;
306
307 bool dark_mode = wxGetApp().app_config->get("dark_color_mode") == "1";
308
309
310 unsigned int sprite_size_px_ex = sprite_size_px + 1;
311
312 m_width = 1 + (int)(sprite_size_px_ex * states.size());
313 m_height = 1 + (int)(sprite_size_px_ex * filenames.size());
314
316 int sprite_n_pixels = sprite_size_px_ex * sprite_size_px_ex;
317 int sprite_stride = sprite_size_px_ex * 4;
318 int sprite_bytes = sprite_n_pixels * 4;
319
320 if (n_pixels <= 0) {
322 return false;
323 }
324
325 std::vector<unsigned char> data(n_pixels * 4, 0);
326 std::vector<unsigned char> sprite_data(sprite_bytes, 0);
327 std::vector<unsigned char> sprite_white_only_data(sprite_bytes, 0);
328 std::vector<unsigned char> sprite_gray_only_data(sprite_bytes, 0);
329 std::vector<unsigned char> output_data(sprite_bytes, 0);
330
331
332 std::vector<unsigned char> pressed_data(sprite_bytes, 0);
333 std::vector<unsigned char> disable_data(sprite_bytes, 0);
334 std::vector<unsigned char> hover_data(sprite_bytes, 0);
335
336 const unsigned char pressed_color[3] = {255, 255, 255};
337 const unsigned char hover_color[3] = {255, 255, 255};
338 const unsigned char normal_color[3] = {43, 52, 54};
339 const unsigned char disable_color[3] = {200, 200, 200};
340 const unsigned char pressed_color_dark[3] = {60, 60, 65};
341 const unsigned char hover_color_dark[3] = {60, 60, 65};
342 const unsigned char normal_color_dark[3] = {182, 182, 182};
343 const unsigned char disable_color_dark[3] = {76, 76, 85};
344
345 NSVGrasterizer* rast = nsvgCreateRasterizer();
346 if (rast == nullptr) {
348 return false;
349 }
350
351 int sprite_id = -1;
352 for (const std::string& filename : filenames) {
353 ++sprite_id;
354
355 if (!boost::filesystem::exists(filename))
356 continue;
357
358 if (!boost::algorithm::iends_with(filename, ".svg"))
359 continue;
360
362 if (image == nullptr)
363 continue;
364
365 float scale = (float)sprite_size_px / std::max(image->width, image->height);
366
367
368 nsvgRasterize(rast, image, 1, 1,
scale, sprite_data.data(), sprite_size_px, sprite_size_px, sprite_stride);
369
370 ::memcpy((void*)pressed_data.data(), (const void*)sprite_data.data(), sprite_bytes);
371 for (int i = 0; i < sprite_n_pixels; ++i) {
373 if (pressed_data.data()[
offset + 0] == 0 &&
374 pressed_data.data()[
offset + 1] == 0 &&
375 pressed_data.data()[
offset + 2] == 0) {
376 hover_data.data()[
offset + 0] = dark_mode ? pressed_color_dark[0] : pressed_color[0];
377 hover_data.data()[
offset + 0] = dark_mode ? pressed_color_dark[1] : pressed_color[1];
378 hover_data.data()[
offset + 0] = dark_mode ? pressed_color_dark[2] : pressed_color[2];
379 }
380 }
381
382 ::memcpy((void*)disable_data.data(), (const void*)sprite_data.data(), sprite_bytes);
383 for (int i = 0; i < sprite_n_pixels; ++i) {
385 if (disable_data.data()[
offset] != 0)
386 ::memset((
void*)&disable_data.data()[
offset], 200, 3);
387 }
388
389 ::memcpy((void*)hover_data.data(), (const void*)sprite_data.data(), sprite_bytes);
390 for (int i = 0; i < sprite_n_pixels; ++i) {
392 if (hover_data.data()[
offset + 0] == 0 &&
393 hover_data.data()[
offset + 1] == 0 &&
394 hover_data.data()[
offset + 2] == 0)
395 {
396 hover_data.data()[
offset + 0] = dark_mode ? hover_color_dark[0] : hover_color[0];
397 hover_data.data()[
offset + 1] = dark_mode ? hover_color_dark[1] : hover_color[1];
398 hover_data.data()[
offset + 2] = dark_mode ? hover_color_dark[2] : hover_color[2];
399 }
400 }
401
402 ::memcpy((void*)sprite_white_only_data.data(), (const void*)sprite_data.data(), sprite_bytes);
403 for (int i = 0; i < sprite_n_pixels; ++i) {
405 if (sprite_white_only_data.data()[
offset + 0] != 0 ||
406 sprite_white_only_data.data()[
offset + 1] != 0 ||
407 sprite_white_only_data.data()[
offset + 2] != 0) {
408 sprite_white_only_data.data()[
offset + 0] = dark_mode ? normal_color_dark[0] : normal_color[0];
409 sprite_white_only_data.data()[
offset + 1] = dark_mode ? normal_color_dark[1] : normal_color[1];
410 sprite_white_only_data.data()[
offset + 2] = dark_mode ? normal_color_dark[2] : normal_color[2];
411 }
412 }
413
414 ::memcpy((void*)sprite_gray_only_data.data(), (const void*)sprite_data.data(), sprite_bytes);
415 for (int i = 0; i < sprite_n_pixels; ++i) {
417 if (sprite_gray_only_data.data()[
offset + 0] != 0 ||
418 sprite_gray_only_data.data()[
offset + 1] != 0 ||
419 sprite_gray_only_data.data()[
offset + 2] != 0) {
420 sprite_gray_only_data.data()[
offset + 0] = dark_mode ? disable_color_dark[0] : disable_color[0];
421 sprite_gray_only_data.data()[
offset + 1] = dark_mode ? disable_color_dark[1] : disable_color[1];
422 sprite_gray_only_data.data()[
offset + 2] = dark_mode ? disable_color_dark[2] : disable_color[2];
423 }
424 }
425
426
427 int sprite_offset_px = sprite_id * (int)sprite_size_px_ex *
m_width;
428 int state_id = -1;
429 for (const std::pair<int, bool>& state : states) {
430 ++state_id;
431
432
433 std::vector<unsigned char>* src = nullptr;
434 switch (state.first)
435 {
436 case 1: { src = &sprite_white_only_data; break; }
437 case 2: { src = &sprite_gray_only_data; break; }
438 default: { src = &hover_data; break; }
439 }
440
441
442 if (state.second) {
443 src = &pressed_data;
444 }
445
446 ::memcpy((void*)output_data.data(), (const void*)src->data(), sprite_bytes);
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465 int state_offset_px = sprite_offset_px + state_id * sprite_size_px_ex;
466 for (int j = 0; j < (int)sprite_size_px_ex; ++j) {
467 ::memcpy((
void*)&data.data()[(state_offset_px + j *
m_width) * 4], (
const void*)&output_data.data()[j * sprite_stride], sprite_stride);
468 }
469 }
470
471 nsvgDelete(image);
472 }
473
474 nsvgDeleteRasterizer(rast);
475
476
482 else
487
489
491
492#if 0
493
494 static int pass = 0;
495 ++pass;
496
498 output.InitAlpha();
499
500 for (
int h = 0; h <
m_height; ++h) {
502 for (
int w = 0; w <
m_width; ++w) {
503 int offset = (px_h + w) * 4;
504 output.SetRGB(w, h, data.data()[
offset + 0], data.data()[
offset + 1], data.data()[
offset + 2]);
505 output.SetAlpha(w, h, data.data()[
offset + 3]);
506 }
507 }
508
509 std::string out_filename =
resources_dir() +
"/images/test_" + std::to_string(pass) +
".png";
510 output.SaveFile(out_filename, wxBITMAP_TYPE_PNG);
511#endif
512
513 return true;
514}