mirror of
https://github.com/juce-framework/JUCE.git
synced 2026-01-09 23:34:20 +00:00
595 lines
22 KiB
C++
595 lines
22 KiB
C++
/*
|
|
==============================================================================
|
|
|
|
This file is part of the JUCE framework.
|
|
Copyright (c) Raw Material Software Limited
|
|
|
|
JUCE is an open source framework subject to commercial or open source
|
|
licensing.
|
|
|
|
By downloading, installing, or using the JUCE framework, or combining the
|
|
JUCE framework with any other source code, object code, content or any other
|
|
copyrightable work, you agree to the terms of the JUCE End User Licence
|
|
Agreement, and all incorporated terms including the JUCE Privacy Policy and
|
|
the JUCE Website Terms of Service, as applicable, which will bind you. If you
|
|
do not agree to the terms of these agreements, we will not license the JUCE
|
|
framework to you, and you must discontinue the installation or download
|
|
process and cease use of the JUCE framework.
|
|
|
|
JUCE End User Licence Agreement: https://juce.com/legal/juce-8-licence/
|
|
JUCE Privacy Policy: https://juce.com/juce-privacy-policy
|
|
JUCE Website Terms of Service: https://juce.com/juce-website-terms-of-service/
|
|
|
|
Or:
|
|
|
|
You may also use this code under the terms of the AGPLv3:
|
|
https://www.gnu.org/licenses/agpl-3.0.en.html
|
|
|
|
THE JUCE FRAMEWORK IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL
|
|
WARRANTIES, WHETHER EXPRESSED OR IMPLIED, INCLUDING WARRANTY OF
|
|
MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, ARE DISCLAIMED.
|
|
|
|
==============================================================================
|
|
*/
|
|
|
|
namespace juce::build_tools
|
|
{
|
|
|
|
Icons Icons::fromFilesSmallAndBig (const File& small, const File& big)
|
|
{
|
|
Icons result;
|
|
result.small = Drawable::createFromImageFile (small);
|
|
result.big = Drawable::createFromImageFile (big);
|
|
return result;
|
|
}
|
|
|
|
Array<const Drawable*> asArray (const Icons& icons)
|
|
{
|
|
Array<const Drawable*> result;
|
|
|
|
for (auto getter : { &Icons::getSmall, &Icons::getBig })
|
|
if (auto* got = (icons.*getter)())
|
|
result.add (got);
|
|
|
|
return result;
|
|
}
|
|
|
|
namespace mac
|
|
{
|
|
static Image fixIconImageSize (const Drawable& image)
|
|
{
|
|
const int validSizes[] = { 16, 32, 64, 128, 256, 512, 1024 };
|
|
|
|
auto w = image.getWidth();
|
|
auto h = image.getHeight();
|
|
|
|
int bestSize = 16;
|
|
|
|
for (int size : validSizes)
|
|
{
|
|
if (w == h && w == size)
|
|
{
|
|
bestSize = w;
|
|
break;
|
|
}
|
|
|
|
if (jmax (w, h) > size)
|
|
bestSize = size;
|
|
}
|
|
|
|
return rescaleImageForIcon (image, bestSize);
|
|
}
|
|
|
|
static void writeIconData (MemoryOutputStream& out, const Image& image, const char* type)
|
|
{
|
|
MemoryOutputStream pngData;
|
|
PNGImageFormat pngFormat;
|
|
pngFormat.writeImageToStream (image, pngData);
|
|
|
|
out.write (type, 4);
|
|
out.writeIntBigEndian (8 + (int) pngData.getDataSize());
|
|
out << pngData;
|
|
}
|
|
} // namespace mac
|
|
|
|
static void writeMacIcon (const Icons& icons, OutputStream& out)
|
|
{
|
|
MemoryOutputStream data;
|
|
auto smallest = std::numeric_limits<int>::max();
|
|
const Drawable* smallestImage = nullptr;
|
|
|
|
const auto images = asArray (icons);
|
|
|
|
for (int i = 0; i < images.size(); ++i)
|
|
{
|
|
auto image = mac::fixIconImageSize (*images[i]);
|
|
jassert (image.getWidth() == image.getHeight());
|
|
|
|
if (image.getWidth() < smallest)
|
|
{
|
|
smallest = image.getWidth();
|
|
smallestImage = images[i];
|
|
}
|
|
|
|
switch (image.getWidth())
|
|
{
|
|
case 16: mac::writeIconData (data, image, "icp4"); break;
|
|
case 32: mac::writeIconData (data, image, "icp5"); break;
|
|
case 64: mac::writeIconData (data, image, "icp6"); break;
|
|
case 128: mac::writeIconData (data, image, "ic07"); break;
|
|
case 256: mac::writeIconData (data, image, "ic08"); break;
|
|
case 512: mac::writeIconData (data, image, "ic09"); break;
|
|
case 1024: mac::writeIconData (data, image, "ic10"); break;
|
|
default: break;
|
|
}
|
|
}
|
|
|
|
jassert (data.getDataSize() > 0); // no suitable sized images?
|
|
|
|
// If you only supply a 1024 image, the file doesn't work on 10.8, so we need
|
|
// to force a smaller one in there too..
|
|
if (smallest > 512 && smallestImage != nullptr)
|
|
mac::writeIconData (data, rescaleImageForIcon (*smallestImage, 512), "ic09");
|
|
|
|
out.write ("icns", 4);
|
|
out.writeIntBigEndian ((int) data.getDataSize() + 8);
|
|
out << data;
|
|
}
|
|
|
|
Image getBestIconForSize (const Icons& icons,
|
|
int size,
|
|
bool returnNullIfNothingBigEnough)
|
|
{
|
|
auto* const im = std::invoke ([&]() -> const Drawable*
|
|
{
|
|
if ((icons.getSmall() != nullptr) != (icons.getBig() != nullptr))
|
|
return icons.getSmall() != nullptr ? icons.getSmall() : icons.getBig();
|
|
|
|
if (icons.getSmall() != nullptr && icons.getBig() != nullptr)
|
|
{
|
|
if (icons.getSmall()->getWidth() >= size && icons.getBig()->getWidth() >= size)
|
|
return icons.getSmall()->getWidth() < icons.getBig()->getWidth() ? icons.getSmall() : icons.getBig();
|
|
|
|
if (icons.getSmall()->getWidth() >= size)
|
|
return icons.getSmall();
|
|
|
|
if (icons.getBig()->getWidth() >= size)
|
|
return icons.getBig();
|
|
}
|
|
|
|
return nullptr;
|
|
});
|
|
|
|
if (im == nullptr)
|
|
return {};
|
|
|
|
if (returnNullIfNothingBigEnough && im->getWidth() < size && im->getHeight() < size)
|
|
return {};
|
|
|
|
return rescaleImageForIcon (*im, size);
|
|
}
|
|
|
|
namespace win
|
|
{
|
|
static void writeBMPImage (const Image& image, const int w, const int h, MemoryOutputStream& out)
|
|
{
|
|
int maskStride = (w / 8 + 3) & ~3;
|
|
|
|
out.writeInt (40); // bitmapinfoheader size
|
|
out.writeInt (w);
|
|
out.writeInt (h * 2);
|
|
out.writeShort (1); // planes
|
|
out.writeShort (32); // bits
|
|
out.writeInt (0); // compression
|
|
out.writeInt ((h * w * 4) + (h * maskStride)); // size image
|
|
out.writeInt (0); // x pixels per meter
|
|
out.writeInt (0); // y pixels per meter
|
|
out.writeInt (0); // clr used
|
|
out.writeInt (0); // clr important
|
|
|
|
Image::BitmapData bitmap (image, Image::BitmapData::readOnly);
|
|
int alphaThreshold = 5;
|
|
|
|
int y;
|
|
for (y = h; --y >= 0;)
|
|
{
|
|
for (int x = 0; x < w; ++x)
|
|
{
|
|
auto pixel = bitmap.getPixelColour (x, y);
|
|
|
|
if (pixel.getAlpha() <= alphaThreshold)
|
|
{
|
|
out.writeInt (0);
|
|
}
|
|
else
|
|
{
|
|
out.writeByte ((char) pixel.getBlue());
|
|
out.writeByte ((char) pixel.getGreen());
|
|
out.writeByte ((char) pixel.getRed());
|
|
out.writeByte ((char) pixel.getAlpha());
|
|
}
|
|
}
|
|
}
|
|
|
|
for (y = h; --y >= 0;)
|
|
{
|
|
int mask = 0, count = 0;
|
|
|
|
for (int x = 0; x < w; ++x)
|
|
{
|
|
auto pixel = bitmap.getPixelColour (x, y);
|
|
|
|
mask <<= 1;
|
|
if (pixel.getAlpha() <= alphaThreshold)
|
|
mask |= 1;
|
|
|
|
if (++count == 8)
|
|
{
|
|
out.writeByte ((char) mask);
|
|
count = 0;
|
|
mask = 0;
|
|
}
|
|
}
|
|
|
|
if (mask != 0)
|
|
out.writeByte ((char) mask);
|
|
|
|
for (int i = maskStride - w / 8; --i >= 0;)
|
|
out.writeByte (0);
|
|
}
|
|
}
|
|
|
|
static void writeIcon (const Array<Image>& images, OutputStream& out)
|
|
{
|
|
out.writeShort (0); // reserved
|
|
out.writeShort (1); // .ico tag
|
|
out.writeShort ((short) images.size());
|
|
|
|
MemoryOutputStream dataBlock;
|
|
|
|
int imageDirEntrySize = 16;
|
|
int dataBlockStart = 6 + images.size() * imageDirEntrySize;
|
|
|
|
for (int i = 0; i < images.size(); ++i)
|
|
{
|
|
auto oldDataSize = dataBlock.getDataSize();
|
|
|
|
auto& image = images.getReference (i);
|
|
auto w = image.getWidth();
|
|
auto h = image.getHeight();
|
|
|
|
if (w >= 256 || h >= 256)
|
|
{
|
|
PNGImageFormat pngFormat;
|
|
pngFormat.writeImageToStream (image, dataBlock);
|
|
}
|
|
else
|
|
{
|
|
writeBMPImage (image, w, h, dataBlock);
|
|
}
|
|
|
|
out.writeByte ((char) w);
|
|
out.writeByte ((char) h);
|
|
out.writeByte (0);
|
|
out.writeByte (0);
|
|
out.writeShort (1); // colour planes
|
|
out.writeShort (32); // bits per pixel
|
|
out.writeInt ((int) (dataBlock.getDataSize() - oldDataSize));
|
|
out.writeInt (dataBlockStart + (int) oldDataSize);
|
|
}
|
|
|
|
jassert (out.getPosition() == dataBlockStart);
|
|
out << dataBlock;
|
|
}
|
|
} // namespace win
|
|
|
|
static void writeWinIcon (const Icons& icons, OutputStream& os)
|
|
{
|
|
Array<Image> images;
|
|
int sizes[] = { 16, 32, 48, 256 };
|
|
|
|
for (int size : sizes)
|
|
{
|
|
auto im = getBestIconForSize (icons, size, true);
|
|
|
|
if (im.isValid())
|
|
images.add (im);
|
|
}
|
|
|
|
if (images.size() > 0)
|
|
win::writeIcon (images, os);
|
|
}
|
|
|
|
void writeMacIcon (const Icons& icons, const File& file)
|
|
{
|
|
writeStreamToFile (file, [&] (MemoryOutputStream& mo) { writeMacIcon (icons, mo); });
|
|
}
|
|
|
|
void writeWinIcon (const Icons& icons, const File& file)
|
|
{
|
|
writeStreamToFile (file, [&] (MemoryOutputStream& mo) { writeWinIcon (icons, mo); });
|
|
}
|
|
|
|
Image rescaleImageForIcon (const Drawable& d, const int size)
|
|
{
|
|
if (auto* drawableImage = dynamic_cast<const DrawableImage*> (&d))
|
|
{
|
|
auto im = SoftwareImageType().convert (drawableImage->getImage());
|
|
|
|
if (im.getWidth() == size && im.getHeight() == size)
|
|
return im;
|
|
|
|
// (scale it down in stages for better resampling)
|
|
while (im.getWidth() > 2 * size && im.getHeight() > 2 * size)
|
|
im = im.rescaled (im.getWidth() / 2,
|
|
im.getHeight() / 2);
|
|
|
|
Image newIm (Image::ARGB, size, size, true, SoftwareImageType());
|
|
Graphics g (newIm);
|
|
g.drawImageWithin (im, 0, 0, size, size,
|
|
RectanglePlacement::centred | RectanglePlacement::onlyReduceInSize, false);
|
|
return newIm;
|
|
}
|
|
|
|
Image im (Image::ARGB, size, size, true, SoftwareImageType());
|
|
Graphics g (im);
|
|
d.drawWithin (g, im.getBounds().toFloat(), RectanglePlacement::centred, 1.0f);
|
|
return im;
|
|
}
|
|
|
|
struct AppIconType
|
|
{
|
|
const char* idiom;
|
|
const char* sizeString;
|
|
const char* filename;
|
|
const char* scale;
|
|
int size;
|
|
};
|
|
|
|
static const AppIconType iOSAppIconTypes[]
|
|
{
|
|
{ "iphone", "20x20", "Icon-Notification-20@2x.png", "2x", 40 },
|
|
{ "iphone", "20x20", "Icon-Notification-20@3x.png", "3x", 60 },
|
|
{ "iphone", "29x29", "Icon-29.png", "1x", 29 },
|
|
{ "iphone", "29x29", "Icon-29@2x.png", "2x", 58 },
|
|
{ "iphone", "29x29", "Icon-29@3x.png", "3x", 87 },
|
|
{ "iphone", "40x40", "Icon-Spotlight-40@2x.png", "2x", 80 },
|
|
{ "iphone", "40x40", "Icon-Spotlight-40@3x.png", "3x", 120 },
|
|
{ "iphone", "60x60", "Icon-60@2x.png", "2x", 120 },
|
|
{ "iphone", "60x60", "Icon-@3x.png", "3x", 180 },
|
|
{ "ipad", "20x20", "Icon-Notifications-20.png", "1x", 20 },
|
|
{ "ipad", "20x20", "Icon-Notifications-20@2x.png", "2x", 40 },
|
|
{ "ipad", "29x29", "Icon-Small-1.png", "1x", 29 },
|
|
{ "ipad", "29x29", "Icon-Small@2x-1.png", "2x", 58 },
|
|
{ "ipad", "40x40", "Icon-Spotlight-40.png", "1x", 40 },
|
|
{ "ipad", "40x40", "Icon-Spotlight-40@2x-1.png", "2x", 80 },
|
|
{ "ipad", "76x76", "Icon-76.png", "1x", 76 },
|
|
{ "ipad", "76x76", "Icon-76@2x.png", "2x", 152 },
|
|
{ "ipad", "83.5x83.5", "Icon-83.5@2x.png", "2x", 167 },
|
|
{ "ios-marketing", "1024x1024", "Icon-AppStore-1024.png", "1x", 1024 }
|
|
};
|
|
|
|
static void createiOSIconFiles (const Icons& icons, File appIconSet)
|
|
{
|
|
auto* imageToUse = icons.getBig() != nullptr ? icons.getBig()
|
|
: icons.getSmall();
|
|
|
|
if (imageToUse != nullptr)
|
|
{
|
|
for (auto& type : iOSAppIconTypes)
|
|
{
|
|
auto image = rescaleImageForIcon (*imageToUse, type.size);
|
|
|
|
if (image.hasAlphaChannel())
|
|
{
|
|
Image background (Image::RGB, image.getWidth(), image.getHeight(), false);
|
|
Graphics g (background);
|
|
g.fillAll (Colours::white);
|
|
|
|
g.drawImageWithin (image, 0, 0, image.getWidth(), image.getHeight(),
|
|
RectanglePlacement::centred | RectanglePlacement::onlyReduceInSize);
|
|
|
|
image = background;
|
|
}
|
|
|
|
MemoryOutputStream pngData;
|
|
PNGImageFormat pngFormat;
|
|
pngFormat.writeImageToStream (image, pngData);
|
|
|
|
overwriteFileIfDifferentOrThrow (appIconSet.getChildFile (type.filename), pngData);
|
|
}
|
|
}
|
|
}
|
|
|
|
static String getiOSAssetContents (var images)
|
|
{
|
|
DynamicObject::Ptr v (new DynamicObject());
|
|
|
|
var info (new DynamicObject());
|
|
info.getDynamicObject()->setProperty ("version", 1);
|
|
info.getDynamicObject()->setProperty ("author", "xcode");
|
|
|
|
v->setProperty ("images", images);
|
|
v->setProperty ("info", info);
|
|
|
|
return JSON::toString (var (v.get()));
|
|
}
|
|
|
|
//==============================================================================
|
|
static String getiOSAppIconContents()
|
|
{
|
|
var images;
|
|
|
|
for (auto& type : iOSAppIconTypes)
|
|
{
|
|
DynamicObject::Ptr d (new DynamicObject());
|
|
d->setProperty ("idiom", type.idiom);
|
|
d->setProperty ("size", type.sizeString);
|
|
d->setProperty ("filename", type.filename);
|
|
d->setProperty ("scale", type.scale);
|
|
images.append (var (d.get()));
|
|
}
|
|
|
|
return getiOSAssetContents (images);
|
|
}
|
|
|
|
struct ImageType
|
|
{
|
|
const char* orientation;
|
|
const char* idiom;
|
|
const char* subtype;
|
|
const char* extent;
|
|
const char* scale;
|
|
const char* filename;
|
|
int width;
|
|
int height;
|
|
};
|
|
|
|
static const ImageType iOSLaunchImageTypes[]
|
|
{
|
|
{ "portrait", "iphone", nullptr, "full-screen", "2x", "LaunchImage-iphone-2x.png", 640, 960 },
|
|
{ "portrait", "iphone", "retina4", "full-screen", "2x", "LaunchImage-iphone-retina4.png", 640, 1136 },
|
|
{ "portrait", "ipad", nullptr, "full-screen", "1x", "LaunchImage-ipad-portrait-1x.png", 768, 1024 },
|
|
{ "landscape","ipad", nullptr, "full-screen", "1x", "LaunchImage-ipad-landscape-1x.png", 1024, 768 },
|
|
{ "portrait", "ipad", nullptr, "full-screen", "2x", "LaunchImage-ipad-portrait-2x.png", 1536, 2048 },
|
|
{ "landscape","ipad", nullptr, "full-screen", "2x", "LaunchImage-ipad-landscape-2x.png", 2048, 1536 }
|
|
};
|
|
|
|
static void createiOSLaunchImageFiles (const File& launchImageSet)
|
|
{
|
|
for (auto& type : iOSLaunchImageTypes)
|
|
{
|
|
Image image (Image::ARGB, type.width, type.height, true); // (empty black image)
|
|
image.clear (image.getBounds(), Colours::black);
|
|
|
|
MemoryOutputStream pngData;
|
|
PNGImageFormat pngFormat;
|
|
pngFormat.writeImageToStream (image, pngData);
|
|
build_tools::overwriteFileIfDifferentOrThrow (launchImageSet.getChildFile (type.filename), pngData);
|
|
}
|
|
}
|
|
|
|
static String getiOSLaunchImageContents()
|
|
{
|
|
var images;
|
|
|
|
for (auto& type : iOSLaunchImageTypes)
|
|
{
|
|
DynamicObject::Ptr d (new DynamicObject());
|
|
d->setProperty ("orientation", type.orientation);
|
|
d->setProperty ("idiom", type.idiom);
|
|
d->setProperty ("extent", type.extent);
|
|
d->setProperty ("minimum-system-version", "7.0");
|
|
d->setProperty ("scale", type.scale);
|
|
d->setProperty ("filename", type.filename);
|
|
|
|
if (type.subtype != nullptr)
|
|
d->setProperty ("subtype", type.subtype);
|
|
|
|
images.append (var (d.get()));
|
|
}
|
|
|
|
return getiOSAssetContents (images);
|
|
}
|
|
|
|
RelativePath createXcassetsFolderFromIcons (const Icons& icons,
|
|
const File& targetFolder,
|
|
String projectFilenameRootString)
|
|
{
|
|
const auto assets = targetFolder.getChildFile (projectFilenameRootString)
|
|
.getChildFile ("Images.xcassets");
|
|
const auto iconSet = assets.getChildFile ("AppIcon.appiconset");
|
|
const auto launchImage = assets.getChildFile ("LaunchImage.launchimage");
|
|
|
|
overwriteFileIfDifferentOrThrow (iconSet.getChildFile ("Contents.json"), getiOSAppIconContents());
|
|
createiOSIconFiles (icons, iconSet);
|
|
|
|
overwriteFileIfDifferentOrThrow (launchImage.getChildFile ("Contents.json"), getiOSLaunchImageContents());
|
|
createiOSLaunchImageFiles (launchImage);
|
|
|
|
return { assets, targetFolder, RelativePath::buildTargetFolder };
|
|
}
|
|
|
|
//==============================================================================
|
|
//==============================================================================
|
|
#if JUCE_UNIT_TESTS
|
|
|
|
class IconsUnitTests : public UnitTest
|
|
{
|
|
public:
|
|
IconsUnitTests() : UnitTest ("Generate icon files", UnitTestCategories::graphics) {}
|
|
|
|
void runTest() override
|
|
{
|
|
const ScopedJuceInitialiser_GUI scope;
|
|
|
|
beginTest ("Load icons from vector file");
|
|
{
|
|
TemporaryFile tempFile ("vector");
|
|
|
|
{
|
|
auto stream = tempFile.getFile().createOutputStream();
|
|
expect (stream != nullptr);
|
|
stream->write (svg, std::size (svg));
|
|
}
|
|
|
|
const auto icons = Icons::fromFilesSmallAndBig (tempFile.getFile(), {});
|
|
|
|
expect (icons.getSmall() != nullptr);
|
|
expect (icons.getBig() == nullptr);
|
|
|
|
expect (dynamic_cast<const DrawableImage*> (icons.getSmall()) == nullptr,
|
|
"Vector data should not be rasterised on load");
|
|
}
|
|
|
|
beginTest ("Load icons from raster file");
|
|
{
|
|
TemporaryFile tempFile ("raster");
|
|
|
|
{
|
|
auto stream = tempFile.getFile().createOutputStream();
|
|
expect (stream != nullptr);
|
|
stream->write (png, std::size (png));
|
|
}
|
|
|
|
const auto icons = Icons::fromFilesSmallAndBig ({}, tempFile.getFile());
|
|
|
|
expect (icons.getSmall() == nullptr);
|
|
expect (icons.getBig() != nullptr);
|
|
|
|
expect (dynamic_cast<const DrawableImage*> (icons.getBig()) != nullptr,
|
|
"Raster data is loaded as a DrawableImage");
|
|
}
|
|
}
|
|
|
|
private:
|
|
static constexpr uint8_t svg[] = R"svg(
|
|
<svg xmlns="http://www.w3.org/2000/svg" width="284.4" height="284.4" viewBox="0 0 284.4 284.4">
|
|
<g>
|
|
<ellipse cx="142.2" cy="142.2" rx="132.82" ry="132.74" fill="#fff"/>
|
|
</g>
|
|
</svg>)svg";
|
|
|
|
static constexpr uint8_t png[]
|
|
{
|
|
0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d,
|
|
0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x07,
|
|
0x08, 0x06, 0x00, 0x00, 0x00, 0xc4, 0x52, 0x57, 0xd3, 0x00, 0x00, 0x00,
|
|
0x5e, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x55, 0x8d, 0x49, 0x0e, 0x00,
|
|
0x21, 0x08, 0x04, 0xfd, 0x89, 0xe2, 0x12, 0x13, 0xf5, 0xea, 0xff, 0x7f,
|
|
0x46, 0x4b, 0x9b, 0xe8, 0x38, 0x87, 0x0a, 0x84, 0x5e, 0x70, 0x21, 0x04,
|
|
0x25, 0xde, 0x7b, 0xcd, 0x39, 0x6b, 0x4a, 0x69, 0xef, 0xc4, 0x89, 0x08,
|
|
0x48, 0xef, 0x1d, 0x63, 0x0c, 0xcc, 0x39, 0xd1, 0x5a, 0xe3, 0xed, 0x13,
|
|
0x2d, 0x71, 0xa1, 0xd1, 0x0c, 0xea, 0xac, 0x12, 0x31, 0x46, 0x58, 0xe5,
|
|
0x86, 0x22, 0x67, 0xad, 0xf5, 0x9f, 0x3c, 0x86, 0x52, 0x0a, 0xee, 0x4f,
|
|
0xa6, 0xdf, 0x6a, 0xee, 0x5b, 0x64, 0xe5, 0x49, 0xbf, 0x50, 0x5c, 0x2f,
|
|
0xb3, 0x44, 0xdf, 0x94, 0x9e, 0x62, 0xe2, 0x00, 0x00, 0x00, 0x00, 0x49,
|
|
0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
|
|
};
|
|
};
|
|
|
|
static IconsUnitTests iconsUnitTests;
|
|
|
|
#endif
|
|
|
|
} // namespace juce::build_tools
|