草庐IT

android - 如何为透明覆盖创建 mask ?

coder 2023-12-13 原文

我有以下场景:一个位图用作背景,另一个位图用作覆盖层,可以是 50% 透明或不透明(在运行时可更改),第三个位图包含第二个掩码位图。我尝试了不同的 Xfermodes 配置和绘图顺序,但未能找到合适的。

我将掩码用作位图,因为我需要能够在程序的两次运行之间或配置更改之间保存它。它是在用户在屏幕上绘制时创建的,有效地清除了 war 迷雾

来自最佳尝试的代码 fragment 。唯一没有像我希望的那样起作用的是我的面具的透明度。

@Override
protected void onDraw(Canvas canvas) {      
    canvas.drawBitmap(mFogOfWar, mTransformationMatrix, mPaintFog);
    canvas.drawBitmap(mMaskBitmap, mTransformationMatrix, mPaintMask);
    canvas.drawBitmap(mImage, mTransformationMatrix, mPaintImage);
}

绘制对象:

mPaintImage.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OVER));
mPaintFog.setAlpha(127);
mPaintMask.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));

这是我通过当前配置得到的更清晰的结果:

我不确定我是否能够在 Paint 对象上设置 alpha;如果不是,我不介意针对 alpha 问题的其他建议或解决方案,最好是不需要重新创建用作 war 迷雾的位图的建议或解决方案。

编辑:

通过执行以下操作,我能够获得我想要的结果:

@Override
protected void onDraw(Canvas canvas) {
    canvas.drawBitmap(mImage, mTransformationMatrix, mPaintImage);
    if (mMaskBitmap != null) {
        canvas.drawBitmap(mFogOfWar, mTransformationMatrix, mPaintFog);
        canvas.drawBitmap(mMaskBitmap, mTransformationMatrix, mPaintMask);
        canvas.drawBitmap(mMaskBitmap, mTransformationMatrix, mPaintImage);
        canvas.drawBitmap(mImage, mTransformationMatrix, mPaintImageSecondPass);
    }

绘制对象:

mPaintImage = new Paint(); // No Xfermode here anymore
mPaintFog.setAlpha(127);
mPaintMask.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
mPaintImageSecondPass.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.LIGHTEN));

但是绘制位图五次似乎是一种浪费。由于 Android 硬件加速,它在 OpenGL 纹理中运行(我将位图重新调整为设备 GPU 可接受的最高分辨率)并且我在我的 invalidates() 中非常小心它运行出奇的流畅在我的 Nexus S 和我的 A500 上,但我不确定其他设备(无论如何项目都将是 4.0+)。

但我相信一定有更好的方法来做到这一点。我想要一种避免 overdraw 太多的方法,或者至少可以向我正确解释那些 Xfermodes 的含义并且我没有 overdraw 东西。

最佳答案

在顿悟之后,我尝试了一种完全不同的方法 - 并意识到这个问题的解决方案是一种更简单的方法 - 通常是这样。因为我只需要两个位图,所以我需要更少的内存来处理它。

绘图:

canvas.drawBitmap(mImage, mTransformationMatrix, mPaintImageRegular);
if (mFogOfWarState != FOG_OF_WAR_HIDDEN) {
    canvas.drawBitmap(mFogOfWar, mTransformationMatrix, mPaintFog);
}

“ secret ”是,我不是在蒙版位图上绘制,而是使用另一种绘制来删除 war 迷雾:

mFogOfWarCanvas.drawPath(mPath, mEraserPaint);

唯一具有XfermodePaint 是用于删除的:

mEraserPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));

为了加载和保存我的面具,我执行以下操作:

private void createFogAndMask(File dataDir) {
    BitmapDrawable tile = (BitmapDrawable) getResources().getDrawable(R.drawable.fog_of_war); 
    tile.setTileModeXY(TileMode.REPEAT, TileMode.REPEAT);
    mFogOfWar = Bitmap.createBitmap(mImageBounds.width(), mImageBounds.height(), Config.ARGB_8888);
    mFogOfWarCanvas = new Canvas(mFogOfWar);
    tile.setBounds(mImageBounds);
    tile.draw(mFogOfWarCanvas);   
    tile = null;

    // Try to load an existing mask
    File existingMask = new File(dataDir, getMaskFileName());
    if (existingMask.exists()) {
        Bitmap existingMaskBitmap = BitmapFactory.decodeFile(existingMask.getAbsolutePath());
        mFogOfWarCanvas.drawBitmap(existingMaskBitmap, new Matrix(), mPaintImageRegular);
        mFogOfWarCanvas.drawPaint(mMaskEraserPaint);
        existingMaskBitmap.recycle();
        System.gc();
    }
}

public void saveMask(File folder) throws IOException {
    if (!mReady || mImagePath == null) return;
    mImage.recycle();
    System.gc();
    if (!folder.exists()) {
        folder.mkdirs();
    }
    File savedFile = new File(folder, getMaskFileName());

    // Change all transparent pixels to black and all non-transparent pixels to transparent
    final int length = mImageBounds.width() * mImageBounds.height();        
    final int[] pixels =  new int[length];
    mFogOfWar.getPixels(pixels, 0, mImageBounds.width(), 0, 0, mImageBounds.width(), mImageBounds.height());
    for (int i = 0; i < length; i++) {
        if (pixels[i] == Color.TRANSPARENT) {
            pixels[i] = Color.BLACK;
        } else {
            pixels[i] = Color.TRANSPARENT;              
        }
    }
    mFogOfWar.setPixels(pixels, 0, mImageBounds.width(), 0, 0, mImageBounds.width(), mImageBounds.height());

    FileOutputStream output = new FileOutputStream(savedFile);
    mFogOfWar.compress(CompressFormat.PNG, 80, output);
    output.flush();
    output.close();     
}

关于android - 如何为透明覆盖创建 mask ?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9305405/

有关android - 如何为透明覆盖创建 mask ?的更多相关文章

  1. ruby - 如何在 Ruby 中顺序创建 PI - 2

    出于纯粹的兴趣,我很好奇如何按顺序创建PI,而不是在过程结果之后生成数字,而是让数字在过程本身生成时显示。如果是这种情况,那么数字可以自行产生,我可以对以前看到的数字实现垃圾收集,从而创建一个无限系列。结果只是在Pi系列之后每秒生成一个数字。这是我通过互联网筛选的结果:这是流行的计算机友好算法,类机器算法:defarccot(x,unity)xpow=unity/xn=1sign=1sum=0loopdoterm=xpow/nbreakifterm==0sum+=sign*(xpow/n)xpow/=x*xn+=2sign=-signendsumenddefcalc_pi(digits

  2. python - 如何使用 Ruby 或 Python 创建一系列高音调和低音调的蜂鸣声? - 2

    关闭。这个问题是opinion-based.它目前不接受答案。想要改进这个问题?更新问题,以便editingthispost可以用事实和引用来回答它.关闭4年前。Improvethisquestion我想在固定时间创建一系列低音和高音调的哔哔声。例如:在150毫秒时发出高音调的蜂鸣声在151毫秒时发出低音调的蜂鸣声200毫秒时发出低音调的蜂鸣声250毫秒的高音调蜂鸣声有没有办法在Ruby或Python中做到这一点?我真的不在乎输出编码是什么(.wav、.mp3、.ogg等等),但我确实想创建一个输出文件。

  3. ruby - 使用 Vim Rails,您可以创建一个新的迁移文件并一次性打开它吗? - 2

    使用带有Rails插件的vim,您可以创建一个迁移文件,然后一次性打开该文件吗?textmate也可以这样吗? 最佳答案 你可以使用rails.vim然后做类似的事情::Rgeneratemigratonadd_foo_to_bar插件将打开迁移生成的文件,这正是您想要的。我不能代表textmate。 关于ruby-使用VimRails,您可以创建一个新的迁移文件并一次性打开它吗?,我们在StackOverflow上找到一个类似的问题: https://sta

  4. ruby-on-rails - 无法使用 Rails 3.2 创建插件? - 2

    我对最新版本的Rails有疑问。我创建了一个新应用程序(railsnewMyProject),但我没有脚本/生成,只有脚本/rails,当我输入ruby./script/railsgeneratepluginmy_plugin"Couldnotfindgeneratorplugin.".你知道如何生成插件模板吗?没有这个命令可以创建插件吗?PS:我正在使用Rails3.2.1和ruby​​1.8.7[universal-darwin11.0] 最佳答案 随着Rails3.2.0的发布,插件生成器已经被移除。查看变更日志here.现在

  5. ruby-on-rails - 在混合/模块中覆盖模型的属性访问器 - 2

    我有一个包含模块的模型。我想在模块中覆盖模型的访问器方法。例如:classBlah这显然行不通。有什么想法可以实现吗? 最佳答案 您的代码看起来是正确的。我们正在毫无困难地使用这个确切的模式。如果我没记错的话,Rails使用#method_missing作为属性setter,因此您的模块将优先,阻止ActiveRecord的setter。如果您正在使用ActiveSupport::Concern(参见thisblogpost),那么您的实例方法需要进入一个特殊的模块:classBlah

  6. ruby - 如何为 emacs 安装 ruby​​-mode - 2

    我刚刚为fedora安装了emacs。我想用emacs编写ruby。为ruby​​提供代码提示、代码完成类型功能所需的工具、扩展是什么? 最佳答案 ruby-mode已经包含在Emacs23之后的版本中。不过,它也可以通过ELPA获得。您可能感兴趣的其他一些事情是集成RVM、feature-mode(Cucumber)、rspec-mode、ruby-electric、inf-ruby、rinari(用于Rails)等。这是我当前用于Ruby开发的Emacs配置:https://github.com/citizen428/emacs

  7. ruby - 如何使用 RSpec::Core::RakeTask 创建 RSpec Rake 任务? - 2

    如何使用RSpec::Core::RakeTask初始化RSpecRake任务?require'rspec/core/rake_task'RSpec::Core::RakeTask.newdo|t|#whatdoIputinhere?endInitialize函数记录在http://rubydoc.info/github/rspec/rspec-core/RSpec/Core/RakeTask#initialize-instance_method没有很好的记录;它只是说:-(RakeTask)initialize(*args,&task_block)AnewinstanceofRake

  8. ruby - 为什么 SecureRandom.uuid 创建一个唯一的字符串? - 2

    关闭。这个问题需要detailsorclarity.它目前不接受答案。想改进这个问题吗?通过editingthispost添加细节并澄清问题.关闭8年前。Improvethisquestion为什么SecureRandom.uuid创建一个唯一的字符串?SecureRandom.uuid#=>"35cb4e30-54e1-49f9-b5ce-4134799eb2c0"SecureRandom.uuid方法创建的字符串从不重复?

  9. ruby - 无法覆盖 irb 中的 to_s - 2

    我在pry中定义了一个函数:to_s,但我无法调用它。这个方法去哪里了,怎么调用?pry(main)>defto_spry(main)*'hello'pry(main)*endpry(main)>to_s=>"main"我的ruby版本是2.1.2看了一些答案和搜索后,我认为我得到了正确的答案:这个方法用在什么地方?在irb或pry中定义方法时,会转到Object.instance_methods[1]pry(main)>defto_s[1]pry(main)*'hello'[1]pry(main)*end=>:to_s[2]pry(main)>defhello[2]pry(main)

  10. ruby - 有人可以帮助解释类创建的 post_initialize 回调吗 (Sandi Metz) - 2

    我正在阅读SandiMetz的POODR,并且遇到了一个我不太了解的编码原则。这是代码:classBicycleattr_reader:size,:chain,:tire_sizedefinitialize(args={})@size=args[:size]||1@chain=args[:chain]||2@tire_size=args[:tire_size]||3post_initialize(args)endendclassMountainBike此代码将为其各自的属性输出1,2,3,4,5。我不明白的是查找方法。当一辆山地自行车被实例化时,因为它没有自己的initialize方法

随机推荐