123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460 |
- /**
- * Copyright (c) 2006-2018 LOVE Development Team
- *
- * This software is provided 'as-is', without any express or implied
- * warranty. In no event will the authors be held liable for any damages
- * arising from the use of this software.
- *
- * Permission is granted to anyone to use this software for any purpose,
- * including commercial applications, and to alter it and redistribute it
- * freely, subject to the following restrictions:
- *
- * 1. The origin of this software must not be misrepresented; you must not
- * claim that you wrote the original software. If you use this software
- * in a product, an acknowledgment in the product documentation would be
- * appreciated but is not required.
- * 2. Altered source versions must be plainly marked as such, and must not be
- * misrepresented as being the original software.
- * 3. This notice may not be removed or altered from any source distribution.
- **/
- // LOVE
- #include "common/config.h"
- #include "Texture.h"
- #include "Graphics.h"
- // C
- #include <cmath>
- #include <algorithm>
- namespace love
- {
- namespace graphics
- {
- love::Type Texture::type("Texture", &Drawable::type);
- Texture::Filter Texture::defaultFilter;
- Texture::FilterMode Texture::defaultMipmapFilter = Texture::FILTER_LINEAR;
- float Texture::defaultMipmapSharpness = 0.0f;
- int64 Texture::totalGraphicsMemory = 0;
- Texture::Texture(TextureType texType)
- : texType(texType)
- , format(PIXELFORMAT_UNKNOWN)
- , readable(true)
- , width(0)
- , height(0)
- , depth(1)
- , layers(1)
- , mipmapCount(1)
- , pixelWidth(0)
- , pixelHeight(0)
- , filter(defaultFilter)
- , wrap()
- , mipmapSharpness(defaultMipmapSharpness)
- , graphicsMemorySize(0)
- {
- }
- Texture::~Texture()
- {
- setGraphicsMemorySize(0);
- }
- void Texture::initQuad()
- {
- Quad::Viewport v = {0, 0, (double) width, (double) height};
- quad.set(new Quad(v, width, height), Acquire::NORETAIN);
- }
- void Texture::setGraphicsMemorySize(int64 bytes)
- {
- totalGraphicsMemory = std::max(totalGraphicsMemory - graphicsMemorySize, (int64) 0);
- bytes = std::max(bytes, (int64) 0);
- graphicsMemorySize = bytes;
- totalGraphicsMemory += bytes;
- }
- TextureType Texture::getTextureType() const
- {
- return texType;
- }
- PixelFormat Texture::getPixelFormat() const
- {
- return format;
- }
- bool Texture::isReadable() const
- {
- return readable;
- }
- bool Texture::isValidSlice(int slice) const
- {
- if (slice < 0)
- return false;
- if (texType == TEXTURE_CUBE)
- return slice < 6;
- else if (texType == TEXTURE_VOLUME)
- return slice < depth;
- else if (texType == TEXTURE_2D_ARRAY)
- return slice < layers;
- else if (slice > 0)
- return false;
- return true;
- }
- void Texture::draw(Graphics *gfx, const Matrix4 &m)
- {
- draw(gfx, quad, m);
- }
- void Texture::draw(Graphics *gfx, Quad *q, const Matrix4 &localTransform)
- {
- using namespace vertex;
- if (!readable)
- throw love::Exception("Textures with non-readable formats cannot be drawn.");
- if (texType == TEXTURE_2D_ARRAY)
- {
- drawLayer(gfx, q->getLayer(), q, localTransform);
- return;
- }
- const Matrix4 &tm = gfx->getTransform();
- bool is2D = tm.isAffine2DTransform();
- Graphics::StreamDrawCommand cmd;
- cmd.formats[0] = vertex::getSinglePositionFormat(is2D);
- cmd.formats[1] = CommonFormat::STf_RGBAub;
- cmd.indexMode = TriangleIndexMode::QUADS;
- cmd.vertexCount = 4;
- cmd.texture = this;
- Graphics::StreamVertexData data = gfx->requestStreamDraw(cmd);
- Matrix4 t(tm, localTransform);
- if (is2D)
- t.transformXY((Vector2 *) data.stream[0], q->getVertexPositions(), 4);
- else
- t.transformXY0((Vector3 *) data.stream[0], q->getVertexPositions(), 4);
- const Vector2 *texcoords = q->getVertexTexCoords();
- vertex::STf_RGBAub *vertexdata = (vertex::STf_RGBAub *) data.stream[1];
- Color c = toColor(gfx->getColor());
- for (int i = 0; i < 4; i++)
- {
- vertexdata[i].s = texcoords[i].x;
- vertexdata[i].t = texcoords[i].y;
- vertexdata[i].color = c;
- }
- }
- void Texture::drawLayer(Graphics *gfx, int layer, const Matrix4 &m)
- {
- drawLayer(gfx, layer, quad, m);
- }
- void Texture::drawLayer(Graphics *gfx, int layer, Quad *q, const Matrix4 &m)
- {
- using namespace vertex;
- if (!readable)
- throw love::Exception("Textures with non-readable formats cannot be drawn.");
- if (texType != TEXTURE_2D_ARRAY)
- throw love::Exception("drawLayer can only be used with Array Textures!");
- if (layer < 0 || layer >= layers)
- throw love::Exception("Invalid layer: %d (Texture has %d layers)", layer + 1, layers);
- Color c = toColor(gfx->getColor());
- const Matrix4 &tm = gfx->getTransform();
- bool is2D = tm.isAffine2DTransform();
- Matrix4 t(tm, m);
- Graphics::StreamDrawCommand cmd;
- cmd.formats[0] = vertex::getSinglePositionFormat(is2D);
- cmd.formats[1] = CommonFormat::STPf_RGBAub;
- cmd.indexMode = TriangleIndexMode::QUADS;
- cmd.vertexCount = 4;
- cmd.texture = this;
- cmd.standardShaderType = Shader::STANDARD_ARRAY;
- Graphics::StreamVertexData data = gfx->requestStreamDraw(cmd);
- if (is2D)
- t.transformXY((Vector2 *) data.stream[0], q->getVertexPositions(), 4);
- else
- t.transformXY0((Vector3 *) data.stream[0], q->getVertexPositions(), 4);
- const Vector2 *texcoords = q->getVertexTexCoords();
- vertex::STPf_RGBAub *vertexdata = (vertex::STPf_RGBAub *) data.stream[1];
- for (int i = 0; i < 4; i++)
- {
- vertexdata[i].s = texcoords[i].x;
- vertexdata[i].t = texcoords[i].y;
- vertexdata[i].p = (float) layer;
- vertexdata[i].color = c;
- }
- }
- int Texture::getWidth(int mip) const
- {
- return std::max(width >> mip, 1);
- }
- int Texture::getHeight(int mip) const
- {
- return std::max(height >> mip, 1);
- }
- int Texture::getDepth(int mip) const
- {
- return std::max(depth >> mip, 1);
- }
- int Texture::getLayerCount() const
- {
- return layers;
- }
- int Texture::getMipmapCount() const
- {
- return mipmapCount;
- }
- int Texture::getPixelWidth(int mip) const
- {
- return std::max(pixelWidth >> mip, 1);
- }
- int Texture::getPixelHeight(int mip) const
- {
- return std::max(pixelHeight >> mip, 1);
- }
- float Texture::getDPIScale() const
- {
- return (float) pixelHeight / (float) height;
- }
- void Texture::setFilter(const Filter &f)
- {
- if (!validateFilter(f, getMipmapCount() > 1))
- {
- if (f.mipmap != FILTER_NONE && getMipmapCount() == 1)
- throw love::Exception("Non-mipmapped texture cannot have mipmap filtering.");
- else
- throw love::Exception("Invalid texture filter.");
- }
- Graphics::flushStreamDrawsGlobal();
- filter = f;
- }
- const Texture::Filter &Texture::getFilter() const
- {
- return filter;
- }
- const Texture::Wrap &Texture::getWrap() const
- {
- return wrap;
- }
- float Texture::getMipmapSharpness() const
- {
- return mipmapSharpness;
- }
- void Texture::setDepthSampleMode(Optional<CompareMode> mode)
- {
- if (mode.hasValue && (!readable || !isPixelFormatDepthStencil(format)))
- throw love::Exception("Only readable depth textures can have a depth sample compare mode.");
- }
- Optional<CompareMode> Texture::getDepthSampleMode() const
- {
- return depthCompareMode;
- }
- Quad *Texture::getQuad() const
- {
- return quad;
- }
- bool Texture::validateFilter(const Filter &f, bool mipmapsAllowed)
- {
- if (!mipmapsAllowed && f.mipmap != FILTER_NONE)
- return false;
- if (f.mag != FILTER_LINEAR && f.mag != FILTER_NEAREST)
- return false;
- if (f.min != FILTER_LINEAR && f.min != FILTER_NEAREST)
- return false;
- if (f.mipmap != FILTER_LINEAR && f.mipmap != FILTER_NEAREST && f.mipmap != FILTER_NONE)
- return false;
- return true;
- }
- int Texture::getTotalMipmapCount(int w, int h)
- {
- return (int) log2(std::max(w, h)) + 1;
- }
- int Texture::getTotalMipmapCount(int w, int h, int d)
- {
- return (int) log2(std::max(std::max(w, h), d)) + 1;
- }
- bool Texture::validateDimensions(bool throwException) const
- {
- bool success = true;
- auto gfx = Module::getInstance<Graphics>(Module::M_GRAPHICS);
- if (gfx == nullptr)
- return false;
- const Graphics::Capabilities &caps = gfx->getCapabilities();
- int max2Dsize = (int) caps.limits[Graphics::LIMIT_TEXTURE_SIZE];
- int max3Dsize = (int) caps.limits[Graphics::LIMIT_VOLUME_TEXTURE_SIZE];
- int maxcubesize = (int) caps.limits[Graphics::LIMIT_CUBE_TEXTURE_SIZE];
- int maxlayers = (int) caps.limits[Graphics::LIMIT_TEXTURE_LAYERS];
- int largestdim = 0;
- const char *largestname = nullptr;
- if ((texType == TEXTURE_2D || texType == TEXTURE_2D_ARRAY) && (pixelWidth > max2Dsize || pixelHeight > max2Dsize))
- {
- success = false;
- largestdim = std::max(pixelWidth, pixelHeight);
- largestname = pixelWidth > pixelHeight ? "pixel width" : "pixel height";
- }
- else if (texType == TEXTURE_2D_ARRAY && layers > maxlayers)
- {
- success = false;
- largestdim = layers;
- largestname = "array layer count";
- }
- else if (texType == TEXTURE_CUBE && (pixelWidth > maxcubesize || pixelWidth != pixelHeight))
- {
- success = false;
- largestdim = std::max(pixelWidth, pixelHeight);
- largestname = pixelWidth > pixelHeight ? "pixel width" : "pixel height";
- if (throwException && pixelWidth != pixelHeight)
- throw love::Exception("Cubemap textures must have equal width and height.");
- }
- else if (texType == TEXTURE_VOLUME && (pixelWidth > max3Dsize || pixelHeight > max3Dsize || depth > max3Dsize))
- {
- success = false;
- largestdim = std::max(std::max(pixelWidth, pixelHeight), depth);
- if (largestdim == pixelWidth)
- largestname = "pixel width";
- else if (largestdim == pixelHeight)
- largestname = "pixel height";
- else
- largestname = "pixel depth";
- }
- if (throwException && largestname != nullptr)
- throw love::Exception("Cannot create texture: %s of %d is too large for this system.", largestname, largestdim);
- return success;
- }
- bool Texture::getConstant(const char *in, TextureType &out)
- {
- return texTypes.find(in, out);
- }
- bool Texture::getConstant(TextureType in, const char *&out)
- {
- return texTypes.find(in, out);
- }
- std::vector<std::string> Texture::getConstants(TextureType)
- {
- return texTypes.getNames();
- }
- bool Texture::getConstant(const char *in, FilterMode &out)
- {
- return filterModes.find(in, out);
- }
- bool Texture::getConstant(FilterMode in, const char *&out)
- {
- return filterModes.find(in, out);
- }
- std::vector<std::string> Texture::getConstants(FilterMode)
- {
- return filterModes.getNames();
- }
- bool Texture::getConstant(const char *in, WrapMode &out)
- {
- return wrapModes.find(in, out);
- }
- bool Texture::getConstant(WrapMode in, const char *&out)
- {
- return wrapModes.find(in, out);
- }
- std::vector<std::string> Texture::getConstants(WrapMode)
- {
- return wrapModes.getNames();
- }
- StringMap<TextureType, TEXTURE_MAX_ENUM>::Entry Texture::texTypeEntries[] =
- {
- { "2d", TEXTURE_2D },
- { "volume", TEXTURE_VOLUME },
- { "array", TEXTURE_2D_ARRAY },
- { "cube", TEXTURE_CUBE },
- };
- StringMap<TextureType, TEXTURE_MAX_ENUM> Texture::texTypes(Texture::texTypeEntries, sizeof(Texture::texTypeEntries));
- StringMap<Texture::FilterMode, Texture::FILTER_MAX_ENUM>::Entry Texture::filterModeEntries[] =
- {
- { "linear", FILTER_LINEAR },
- { "nearest", FILTER_NEAREST },
- { "none", FILTER_NONE },
- };
- StringMap<Texture::FilterMode, Texture::FILTER_MAX_ENUM> Texture::filterModes(Texture::filterModeEntries, sizeof(Texture::filterModeEntries));
- StringMap<Texture::WrapMode, Texture::WRAP_MAX_ENUM>::Entry Texture::wrapModeEntries[] =
- {
- { "clamp", WRAP_CLAMP },
- { "clampzero", WRAP_CLAMP_ZERO },
- { "repeat", WRAP_REPEAT },
- { "mirroredrepeat", WRAP_MIRRORED_REPEAT },
- };
- StringMap<Texture::WrapMode, Texture::WRAP_MAX_ENUM> Texture::wrapModes(Texture::wrapModeEntries, sizeof(Texture::wrapModeEntries));
- } // graphics
- } // love
|