iis服务器助手广告广告
返回顶部
首页 > 资讯 > 移动开发 >Android 使用压缩纹理的方案
  • 671
分享到

Android 使用压缩纹理的方案

2024-04-02 19:04:59 671人浏览 八月长安
摘要

目录一、压缩纹理概念二、OpenGL 接口1.glCompressedTexImage2D2.判断压缩纹理是否支持三、压缩纹理加载1.ETC12.ETC23.ASTC四、总结本文介绍

本文介绍了什么是压缩纹理,以及加载压缩纹理的核心步骤。并在 Android OpenGLES 平台上实现了压缩纹理的显示。

一、压缩纹理概念

传统的图片文件格式有 PNG 、 JPEG 等,这种类型的图片格式无法直接被 GPU 读取,需要先经过 CPU 解码后再上传到 GPU 使用,解码后的数据以 RGB(A) 形式存储,无压缩。

纹理压缩顾名思义是一种压缩的纹理格式,它通常会将纹理划分为固定大小的块(block)或者瓦片(tile),每个块单独进行压缩,整体显存占用更低,并且能直接被 GPU 读取和渲染(无需 CPU 解码)。

纹理压缩支持随机访问,随机访问是很重要的特性,因为纹理访问的模式高度随机,只有在渲染时被用到的部分才需要访问到,且无法提前预知其顺序。而且在场景中相邻的像素在纹理中不一定是相邻的 ,因此图形渲染性能高度依赖于纹理访问的效率。综上,相比普通格式图片,纹理压缩可以节省大量显存和 CPU 解码时间,且对 GPU 友好。

二、OpenGL 接口

想要使用 OpenGL 加载压缩纹理,只需要了解一个接口:glCompressedTexImage2D

1.glCompressedTexImage2D

接口声明如下,注释里说明了各参数的含义:

void glCompressedTexImage2D (GLenum target, 
                             GLint level, 
                             GLenum internalfORMat, // 格式
                             GLsizei width,  // 纹理宽度
                             GLsizei height, // 纹理高度
                             GLint border, 
                             GLsizei imageSize, // 纹理数据大小
                             const void *data) // 纹理数据

所以加载一个压缩纹理,主要有以下几个要点:

  • 获取到压缩纹理存储格式
  • 获取压缩纹理的大小
  • 获取压缩纹理的图像大小

2.判断压缩纹理是否支持

有的设备可能不支持压缩纹理,使用前需要进行判断。

std::string extensions = (const char*)glGetString(GL_EXTENSIONS);
if (extensions.find("GL_OES_compressed_ETC1_RGB8_texture")!= string::npos) {
 // 支持 ETC1 纹理
}

if (extensions.find("GL_OES_texture_compression_astc") != std::string::npos) {
 // 支持 ASTC 纹理
}

三、压缩纹理加载

为了方便描述,定义了一个压缩纹理的结构体:

// 压缩纹理相关信息
struct CompressedTextureInfo {
 bool is_valid; // 是否为一个有效的压缩纹理信息
 GLsizei width;
 GLsizei height;
 GLsizei size;
 GLenum internal_format;
 GLvoid *data;
};

下面介绍ETC1、ETC2和ASTC格式的压缩纹理如何解析成CompressedTextureInfo对象。

1.ETC1

ETC1格式是OpenGL ES图形标准的一部分,并且被所有的Android设备所支持。
扩展名为: GL_OES_compressed_ETC1_RGB8_texture,不支持透明通道,所以仅能用于不透明纹理。且要求大小是2次幂。
当加载压缩纹理时,参数支持如下格式: GL_ETC1_RGB8_OES(RGB,每个像素0.5个字节)
ETC1 压缩纹理的加载,主要参考了Android源码:etc1.cpp
解析 ETC1 纹理:

// 解析 ETC1 纹理
static const CompressedTextureInfo ParseETC1Texture(unsigned char* data) {
    CompressedTextureInfo textureInfo;
    textureInfo.is_valid = false;
    const etc1::etc1_byte *header = data;
    if (!etc1::etc1_pkm_is_valid(header)) {
        LogE("LoadTexture: etc1_pkm is not valid");
        return textureInfo;
    }
    unsigned int width = etc1::etc1_pkm_get_width(header);
    unsigned int height = etc1::etc1_pkm_get_height(header);
    GLuint size = 8 * ((width + 3) >> 2) * ((height + 3) >> 2);
    GLvoid *texture_data = data + ETC1_PKM_HEADER_SIZE;
    textureInfo.is_valid = true;
    textureInfo.width = width;
    textureInfo.height = height;
    textureInfo.size = size;
    textureInfo.internal_format = GL_ETC1_RGB8_OES;
    textureInfo.data = texture_data;
    return textureInfo;
}

2.ETC2

ETC2 是 ETC1 的扩展,压缩比率一样,但压缩质量更高,而且支持透明通道,能完整存储 RGBA 信息。ETC2 需要 OpenGL ES 3.0(对应 webGL 2.0)环境,目前还有不少低端 Android 手机不兼容,iOS 方面从 iPhone5S 开始都支持 OpenGL ES 3.0。ETC2 和 ETC1 一样,长宽可以不相等,但要求是 2 的幂次方。

首先定义好 ETC2 的 Header:

// etc2_texture.h
class Etc2Header {
public:
    Etc2Header(const unsigned char *data);
    unsigned short getWidth(void) const;
    unsigned short getHeight(void) const;
    unsigned short getPaddedWidth(void) const;
    unsigned short getPaddedHeight(void) const;
    GLsizei getSize(GLenum internalFormat) const;

private:
    unsigned char paddedWidthMSB;
    unsigned char paddedWidthLSB;
    unsigned char paddedHeightMSB;
    unsigned char paddedHeightLSB;
    unsigned char widthMSB;
    unsigned char widthLSB;
    unsigned char heightMSB;
    unsigned char heightLSB;
};


// etc2_texture.cpp
Etc2Header::Etc2Header(const unsigned char *data) {
    paddedWidthMSB  = data[8];
    paddedWidthLSB  = data[9];
    paddedHeightMSB = data[10];
    paddedHeightLSB = data[11];
    widthMSB        = data[12];
    widthLSB        = data[13];
    heightMSB       = data[14];
    heightLSB       = data[15];
}

unsigned short Etc2Header::getWidth() const {
    return (widthMSB << 8) | widthLSB;
}

unsigned short Etc2Header::getHeight() const {
    return (heightMSB << 8) | heightLSB;
}

unsigned short Etc2Header::getPaddedWidth() const {
    return (paddedWidthMSB << 8) | paddedWidthLSB;
}

unsigned short Etc2Header::getPaddedHeight() const {
    return (paddedHeightMSB << 8) | paddedHeightLSB;
}

GLsizei Etc2Header::getSize(GLenum internalFormat) const {
    if (internalFormat != GL_COMPRESSED_RG11_EAC
        && internalFormat != GL_COMPRESSED_SIGNED_RG11_EAC
        && internalFormat != GL_COMPRESSED_RGBA8_ETC2_EAC
        && internalFormat != GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC) {
        return (getPaddedWidth() * getPaddedHeight()) >> 1;
    }
    return (getPaddedWidth() * getPaddedHeight());
}

解析 ETC2 数据:

// ETC2 魔数
static const char kMagic[] = { 'P', 'K', 'M', ' ', '2', '0' };

static const bool IsEtc2Texture(unsigned char *data) {
    return memcmp(data, kMagic, sizeof(kMagic)) == 0;
}

static const CompressedTextureInfo ParseETC2Texture(unsigned char *data, GLenum internal_format) {
    CompressedTextureInfo textureInfo;
    textureInfo.is_valid = false;
    if (!IsEtc2Texture(data)) {
        LogE("ParseETC2Texture: not a etc2 texture");
        return textureInfo;
    }
    Etc2Header etc2Header(data);
    textureInfo.is_valid = true;
    textureInfo.width = etc2Header.getWidth();
    textureInfo.height = etc2Header.getHeight();
    textureInfo.size = etc2Header.getSize(internal_format);
    textureInfo.internal_format = internal_format;
    textureInfo.data = data + ETC2_PKM_HEADER_SIZE;
    return textureInfo;
}

3.ASTC

由ARM & AMD研发。ASTC同样是基于block的压缩方式,但块的大小却较支持多种尺寸,比如从基本的4x4到12x12;每个块内的内容用128bits来进行存储,因而不同的块就对应着不同的压缩率;相比ETC,ASTC不要求长宽是2的幂次方。

// ASTC 魔数
const unsigned char ASTC_MAGIC_NUMBER[] = {0x13, 0xAB, 0xA1, 0x5C};

// ASTC header declaration
typedef struct
{
    unsigned char  magic[4];
    unsigned char  blockdim_x;
    unsigned char  blockdim_y;
    unsigned char  blockdim_z;
    unsigned char  xsize[3];   
    unsigned char  ysize[3];   
    unsigned char  zsize[3];   
} AstcHeader;

static const bool IsAstcTexture(unsigned char* buffer) {
    return memcmp(buffer, ASTC_MAGIC_NUMBER, sizeof(ASTC_MAGIC_NUMBER)) == 0;
}

static const CompressedTextureInfo ParseAstcTexture(unsigned char *data, GLenum internal_format) {
    CompressedTextureInfo textureInfo;
    textureInfo.is_valid = false;
    if (internal_format < GL_COMPRESSED_RGBA_ASTC_4x4_KHR
        || internal_format > GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR) {
        LogE("parseAstcTexture: invalid internal_format=%d", internal_format);
        return textureInfo;
    }

    if (!IsAstcTexture(data)) {
        LogE("parseAstcTexture: not a astc file.");
        return textureInfo;
    }
    // 映射为 ASTC 头
    AstcHeader* astc_data_ptr = (AstcHeader*) data;

    int x_size = astc_data_ptr->xsize[0] + (astc_data_ptr->xsize[1] << 8) + (astc_data_ptr->xsize[2] << 16);
    int y_size = astc_data_ptr->ysize[0] + (astc_data_ptr->ysize[1] << 8) + (astc_data_ptr->ysize[2] << 16);
    int z_size = astc_data_ptr->zsize[0] + (astc_data_ptr->zsize[1] << 8) + (astc_data_ptr->zsize[2] << 16);

    int x_blocks = (x_size + astc_data_ptr->blockdim_x - 1) / astc_data_ptr->blockdim_x;
    int y_blocks = (y_size + astc_data_ptr->blockdim_y - 1) / astc_data_ptr->blockdim_y;
    int z_blocks = (z_size + astc_data_ptr->blockdim_z - 1) / astc_data_ptr->blockdim_z;

    unsigned int n_bytes_to_read = x_blocks * y_blocks * z_blocks << 4;

    textureInfo.is_valid = true;
    textureInfo.internal_format = internal_format;
    textureInfo.width = x_size;
    textureInfo.height = y_size;
    textureInfo.size = n_bytes_to_read;
    textureInfo.data = data;
    return textureInfo;
}

得到CompressedTextureInfo对象后,即可进行压缩纹理的显示了:

CompressedTextureInfo textureInfo = etc1::ParseETC1Texture(input_data);
if (!textureInfo.is_valid) {
    LogE("LoadTexture: etc1 textureInfo parsed invalid.");
}
GLuint texture_id = 0;
glGenTextures(1, &texture_id);
glBindTexture(GL_TEXTURE_2D, texture_id);
glCompressedTexImage2D(GL_TEXTURE_2D,
                       0,
                       textureInfo.internal_format,
                       textureInfo.width,
                       textureInfo.height,
                       0,
                       textureInfo.size,
                       textureInfo.data);

四、总结

压缩纹理的加载,主要是搞清楚如何解析压缩纹理数据。一般而言,压缩纹理加载到内存后,都有一个 Header,通过 Header 可以解析出其宽高等信息,计算出纹理图像大小。最后调用glCompressedTexImage2D方法即可渲染。
可见压缩纹理完全没有图像的解码工作,大大提升加载速度。

最后,介绍几款纹理压缩工具

  • etc2comp:支持生成etc2纹理
  • etc1tool:支持生成etc1纹理,在Android SDK目录下,Android/sdk/platform-tools/etc1tool
  • ISPCTextureCompressor:支持etc1、astc等
  • astc-encoder:ASTC官方编码器

到此这篇关于Android 使用压缩纹理的文章就介绍到这了,更多相关Android压缩纹理内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

--结束END--

本文标题: Android 使用压缩纹理的方案

本文链接: https://www.lsjlt.com/news/168631.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • Android 使用压缩纹理的方案
    目录一、压缩纹理概念二、OpenGL 接口1.glCompressedTexImage2D2.判断压缩纹理是否支持三、压缩纹理加载1.ETC12.ETC23.ASTC四、总结本文介绍...
    99+
    2024-04-02
  • 使用vue打包时gzip压缩的两种方案
    目录介绍两种gzip压缩的方式webpack打包生成gz文件服务器在线gzip压缩vue项目开启gzip压缩前端配置后端配置开局一张图: 可以看出,在项目部署后,我们的资源文件请求...
    99+
    2024-04-02
  • Android Tiny集成图片压缩框架的使用
    为了简化对图片压缩的调用,提供最简洁与合理的api压缩逻辑,对于压缩为Bitmap根据屏幕分辨率动态适配最佳大小,对于压缩为File优化底层libjpeg的压缩,整个图片压缩过程全在压缩线程池中异步压缩,结束后分发回UI线程。支持的压缩类型...
    99+
    2023-05-30
    android 图片 压缩
  • Android数据压缩的方法是什么
    本文小编为大家详细介绍“Android数据压缩的方法是什么”,内容详细,步骤清晰,细节处理妥当,希望这篇“Android数据压缩的方法是什么”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧。1. 前言在开发中我们难免...
    99+
    2023-07-05
  • Linux中常用的压缩和解压缩命令整理
    本篇内容主要讲解“Linux中常用的压缩和解压缩命令整理”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“Linux中常用的压缩和解压缩命令整理”吧!文件后缀解压/解包压缩/打包备注*.tartar...
    99+
    2023-06-13
  • Android-图片压缩详解:原理、方法与实践
    前言1. 图片压缩的原理2. Android图片压缩的方法2.1 BitmapFactory.Options2.1.1 尺寸压缩2.1.2 质量压缩 2.2 第三方库 3. 实践:Android图片压缩3.1 创建...
    99+
    2023-08-18
    android android studio ide
  • Android三种常见的图片压缩方式
    目录1、质量压缩2、按比例压缩(尺寸压缩、采样率压缩)3、鲁班压缩(推荐)下面就为大家带来3种比较常见的压缩方式 先给出一组数据 原图:width:2976; height:297...
    99+
    2024-04-02
  • JS/CSS压缩工具的使用方法
    这篇文章主要讲解了“JS/CSS压缩工具的使用方法”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“JS/CSS压缩工具的使用方法”吧!网页访问速度慢如何提高呢...
    99+
    2024-04-02
  • Linux压缩打包命令的使用方法
    本篇内容主要讲解“Linux压缩打包命令的使用方法”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“Linux压缩打包命令的使用方法”吧!参数:-c :建立一个压缩文件的参数指令(create 的意...
    99+
    2023-06-13
  • Android常见的图片压缩方式有哪些
    小编给大家分享一下Android常见的图片压缩方式有哪些,希望大家阅读完这篇文章之后都有所收获,下面让我们一起去探讨吧!先给出一组数据原图:width:2976; height:2976原图实际:--->byte:2299820 Mb...
    99+
    2023-06-15
  • Android 基于Bitmap的四种图片压缩方式
    目录知识点介绍 正文 1、质量压缩 2、采样率压缩 3、缩放法压缩 4、RGB_565 通过改变图片格式来实现压缩 总结 知识点介绍 Android 中图片主要以 Bitmap 的...
    99+
    2024-04-02
  • python对gif图压缩的完美解决方案
    目录1. 背景:2.寻找解决办法2.1可行性分析3.问题分解3.1将gif文件分解3.1.1示例代码3.1.2 效果图4.代码优化5.做成一个通用工具5.1创建一个虚拟环境5.2虚拟...
    99+
    2024-04-02
  • Android应用中实现图片压缩的方法有哪些
    这篇文章将为大家详细讲解有关Android应用中实现图片压缩的方法有哪些,文章内容质量较高,因此小编分享给大家做个参考,希望大家阅读完这篇文章后对相关知识有一定的了解。Android图片压缩几种方式总结图片压缩在Android开发中很常见也...
    99+
    2023-05-31
    android roi
  • Linux中tar压缩档案管理命令怎么用
    这篇文章主要介绍了Linux中tar压缩档案管理命令怎么用,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。概要: tar主要进行档案的压缩与解压缩,是比较常用的命令。 命令格式...
    99+
    2023-06-13
  • Android图片实现压缩处理的实例代码
    整理文档,搜刮出一个Android图片实现压缩处理的实例代码,稍微整理精简一下做下分享。详解:获取本地图片File文件 获取BitmapFactory.Options对象 计算原始图片 目标图片宽高比 计算输出的图片宽高根据...
    99+
    2023-05-30
    android 图片 压缩
  • JAVA使用ffmepg处理视频的方法(压缩,分片,合并)
    FFmepg安装 路径: 然后在使用的类中生命一个全局变量就好 private static String ffmpegPath = "C:\\hk\\ffmpeg\\bi...
    99+
    2024-04-02
  • FSG压缩壳和ImportREC的使用
    FSG压缩壳和ImportREC是两种常用的反调试和反逆向工具。以下是它们的使用方法:1. FSG压缩壳:- FSG压缩壳是一种可执...
    99+
    2023-09-12
    FSG
  • springboot对压缩请求的处理方法
    目录springboot对压缩请求的处理一、Tomcat设置压缩原理二、银联报文压缩补充:java springbooot使用gzip压缩字符串springboot对压缩请求的处理 ...
    99+
    2023-05-18
    springboot压缩请求 springboot请求
  • 使用css写带纹理渐变背景图的案例
    本文将为大家详细介绍“使用css写带纹理渐变背景图的案例”,内容步骤清晰详细,细节处理妥当,而小编每天都会更新不同的知识点,希望这篇“使用css写带纹理渐变背景图的案例”能够给你意想不到的收获,请大家跟着小编的思路慢慢深入,具体内容如下,一...
    99+
    2023-06-08
  • Android性能优化之图片大小,尺寸压缩综合解决方案
    目录前言常见的图片压缩方法质量压缩尺寸压缩libjpeg图片压缩流程总结前言 在Android中我们经常会遇到图片压缩的场景,比如给服务端上传图片,包括个人信息的用户头像,有时候人脸...
    99+
    2024-04-02
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作