从Android ④2到Android420ToARGB:色彩管理与显示技术的革新之旅
在移动科技日新月异的今天,Android操作系统作为智能设备的核心驱动力,其每一次迭代都引领着行业风向标。从Android ④2 Jelly Bean到更先进的版本(虽然“Android420”并非官方版本命名,这里我们假设它代表Android生态系统中色彩管理与显示技术的一个假设性高峰),Android系统在色彩管理、图像渲染以及用户界面体验上实现了质的飞跃,尤其是Android420ToARGB这一假想的色彩转换技术,更是将显示技术推向了新的高度。
一、Android ④2:色彩管理的初步探索
回溯到2012年发布的Android ④2,这一版本虽然以性能优化和用户体验提升为主打,但在色彩管理方面也迈出了重要一步。Android ④2引入了更精细的显示校准功能,使得屏幕色彩更加准确,为用户带来了更真实的视觉享受。这一时期的Android开始注重色彩空间的定义,为后续的色彩管理打下了坚实的基础。尤其是照片和视频应用中,Android ④2通过增强色彩映射算法,让图像色彩更加丰富,细节表现更为细腻,为用户提供了更接近现实的视觉体验。
二、色彩管理技术的演进之路
随着技术的不断进步,Android系统在色彩管理方面持续深耕。从Android ⑤0 Lollipop开始,Android引入了Material Design设计语言,这不仅是一场界面设计的革命,更是色彩管理策略的一次全面升级。Material Design强调色彩的对比与和谐,要求系统能够更精准地控制色彩表现,以适应不同设备和屏幕类型的需求。在此后的版本中,Android不断优化色彩管理框架,提高了色彩转换的效率与精度,为开发者提供了更为灵活的色彩工具集,使得应用程序能够呈现出更加丰富、细腻的色彩效果。
三、假想技术:Android420ToARGB的色彩转换艺术
虽然“Android420”并非实际存在的Android版本,但借此构想探讨未来可能的色彩转换技术——Android420ToARGB,这一概念代表着从一种色彩空间(假设为某种高级别的4:2:0色度子采样格式)到ARGB(Alpha, Red, Green, Blue)色彩空间的转换技术。在Android420ToARGB的设想中,系统能够以前所未有的精度进行色彩转换,不仅保留了源色彩空间的所有色彩信息,还能根据目标设备特性进行智能调整,实现色彩的精准还原与优化。这对于HDR(高动态范围)内容的显示尤为重要,能够显著提升画面的动态范围和细节层次,为用户带来沉浸式的视觉盛宴。此外,Android4
关于Android 中 Options.inSampleSize怎么用
转自:
这个是读取bitmap时用到的属性,是为了对原图降采样.
比如原图是一个 4000 * 4000 点阵的图,占用内存就是 4000 * 4000 * 单个像素占用字节数
单个像素占用字节数取决于你用的是 RGB565 , ARGB8888 等. 4000 * 4000 这个解析度已很接近目前市面主流机器的默认照片解析度.
假设你用的是 RGB565 解析这张图,那一个点就占用2个字节.如果完整解析这个图片,就需要 大约3.2MB的内存.
如果你用了一个GridView,同时显示了30张这种图,那几乎可以确定你会收到一个OOM异常.
所以需要对这种大图进行降采样,以减小内存占用.毕竟拇指大小的地方根本用不着显示那么高的解析度.
因为直接从点阵中隔行抽取最有效率,所以为了兼顾效率, inSampleSize 这个属性只认2的整数倍为有效.
比如你将 inSampleSize 赋值为2,那就是每隔2行采1行,每隔2列采一列,那你解析出的图片就是原图大小的1/4.
这个值也可以填写非2的倍数,非2的倍数会被四舍五入.
综上,用这个参数解析bitmap就是为了减少内存占用.
一下是我的一个根据指定大小对图片文件降采样读取的一个函数,供参考.
/**
* author: liuxu
* de-sample according to given width and height. if required width or height is
* smaller than the origin picture's with or height, de-sample it.
* NOTE: if image quality is your first concern, do not use this method.
* @param path full path for the picture
* @param width the required width
* @param height the required height
* @return bitmap
*/
public static Bitmap decodeBitmapForSize(String path, int width, int height) {
final BitmapFactory.Options options = new BitmapFactory.Options();
if (width != 0 && height != 0) {
// decode with inJustDecodeBounds=true to check size
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(path, options);
// calculate inSampleSize according to the requested size
options.inSampleSize = calculateInSampleSize(options, width, height);
options.inJustDecodeBounds = false;
}
// decode bitmap with the calculated inSampleSize
options.inPreferredConfig = Config.RGB_565;
options.inPurgeable = true;
options.inInputShareable = true;
return BitmapFactory.decodeFile(path, options);
}
/**
* author: liuxu
* de-sample according to given width and height
* @param options options
* @param reqWidth the required width
* @param reqHeight the required height
* @return the calculated sample size
*/
private static int calculateInSampleSize(BitmapFactory.Options options,
int reqWidth, int reqHeight) {
// raw height and width of image
final int height = options.outHeight;
final int width = options.outWidth;
int initSize = 1;
if (height > reqHeight || width > reqWidth) {
if (width > height) {
initSize = Math.round((float) height / (float) reqHeight);
} else {
initSize = Math.round((float) width / (float) reqWidth);
}
}
/*
* the function rounds up the sample size to a power of 2 or multiple of 8 because
* BitmapFactory only honors sample size this way. For example, BitmapFactory
* down samples an image by 2 even though the request is 3.
*/
int roundedSize;
if (initSize <= 8) {
roundedSize = 1;
while (roundedSize < initSize) {
roundedSize <<= 1;
}
} else {
roundedSize = (initSize + 7) / 8 * 8;
}
return roundedSize;
}
android 背景虚化原理是什么
1. RenderScript
谈到高斯模糊,第一个想到的就是RenderScript。RenderScript是由Android3.0引入,用来在Android上编写高性能代码的一种语言(使用C99标准)。 引用官方文档的描述:
RenderScript runtime will parallelize work across all processors available on a device, such as multi-core CPUs, GPUs, or DSPs, allowing you to focus on expressing algorithms rather than scheduling work or load balancing.
为了在Android中使用RenderScript,我们需要(直接贴官方文档,比直译更通俗易懂):
High-performance compute kernels are written in a C99-derived language.
A Java API is used for managing the lifetime of RenderScript resources and controlling kernel execution.
学习文档:
上面两点总结成一句话为:我们需要一组compute kernels(.rs文件中编写),及一组用于控制renderScript相关的java api(.rs文件自动生成为java类)。 由于compute kernels的编写需要一定的学习成本,从JELLY_BEAN_MR1开始,Androied内置了一些compute kernels用于常用的操作,其中就包括了Gaussian blur。
下面,通过实操来讲解一下RenderScript来实现高斯模糊,最终实现效果(讲文字背景进行模糊处理):
布局:
[html] view plaincopy
<xml version="1.0" encoding="utf-8">
<FrameLayout xmlns:android=";
android:layout_width="match_parent"
android:layout_height="match_parent" >
<ImageView
android:id="@+id/picture"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:src="@drawable/splash"
android:scaleType="centerCrop" />
<TextView
android:id="@+id/text"
android:gravity="center_horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Gaussian Blur"
android:textColor="@android:color/black"
android:layout_gravity="center_vertical"
android:textStyle="bold"
android:textSize="48sp" />
<LinearLayout
android:id="@+id/controls"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#7f000000"
android:orientation="vertical"
android:layout_gravity="bottom" />
</FrameLayout>
核心代码:
[java] view plaincopy
private void applyBlur() {
image.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
@Override
public boolean onPreDraw() {
image.getViewTreeObserver().removeOnPreDrawListener(this);
image.buildDrawingCache();
Bitmap bmp = image.getDrawingCache();
blur(bmp, text, true);
return true;
}
});
}
@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
private void blur(Bitmap bkg, View view) {
long startMs = System.currentTimeMillis();
float radius = 20;
Bitmap overlay = Bitmap.createBitmap((int)(view.getMeasuredWidth()), (int)(view.getMeasuredHeight()), Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(overlay);
canvas.translate(-view.getLeft(), -view.getTop());
canvas.drawBitmap(bkg, 0, 0, null);
RenderScript rs = RenderScript.create(SecondActivity.this);
Allocation overlayAlloc = Allocation.createFromBitmap(rs, overlay);
ScriptIntrinsicBlur blur = ScriptIntrinsicBlur.create(rs, overlayAlloc.getElement());
blur.setInput(overlayAlloc);
blur.setRadius(radius);
blur.forEach(overlayAlloc);
overlayAlloc.copyTo(overlay);
view.setBackground(new BitmapDrawable(getResources(), overlay));
rs.destroy();
statusText.setText("cost " + (System.currentTimeMillis() - startMs) + "ms");
}
当ImageView开始加载背景图时,取出它的drawableCache,进行blur处理,Gaussian blur的主要逻辑在blur函数中。对于在Java中使用RenderScript,文档中也有详细描述,对应到我们的代码,步骤为:
初始化一个RenderScript Context.
至少创建一个Allocation对象用于存储需要处理的数据.
创建compute kernel的实例,本例中是内置的ScriptIntrinsicBlur对象.
设置ScriptIntrinsicBlur实例的相关属性,包括Allocation, radius等.
开始blur操作,对应(forEach).
将blur后的结果拷贝回bitmap中。
怎样在Android屏幕上循环的输出一个文字
效果图:
实现的方法:
在layout中这样来声明:
<com.kaixin001.view.ScrollText android:id="@+id/news_statustxt"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingLeft="10dp"
android:paddingRight="10dp"
android:layout_gravity="center_vertical"
android:textColor="#000000"
android:textSize="16sp"
android:hint="@string/news_state_hint"
android:inputType="text"/>
activity这样来调用:
private void initStatus(){
ivState = (ImageView) findViewById(R.id.news_statusinput);
//必须使text长度比控件的宽度大
String s = "dsafsdfsdf(#开心)fsgfdg(#闭嘴)fdgdfgdfgdfgdfgdfgfdgfdgfdgfdgdfg";
newsModel.setStatus(s);
String strModel = newsModel.getStatus();
setStateText(strModel);
}
private void setStateText(String strModel){
if(!TextUtils.isEmpty(strModel)){
tvState.setStateList(newsModel.getStateList());
tvState.setText(strModel);
tvState.init(getWindowManager(), handler);
tvState.startScroll();
tvState.start();
}
}
<pre name="code" class="java"> <span style="white-space:pre"> </span> public void setStatus(String status){
this.status = status;
if(!TextUtils.isEmpty(status)){
stateList = ParseNewsInfoUtil.parseStr(status);
}
<span style="white-space:pre"> </span>}
private Handler handler = new Handler() {@Overridepublic void handleMessage(Message msg) {switch (msg.what) {case ScrollText.TEXT_TIMER:if(tvState != null){tvState.scrollText();}break;default:break;}}};
import java.util.ArrayList;
import java.util.Timer;
import java.util.TimerTask;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Bitmap.Config;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Paint.FontMetrics;
import android.graphics.Paint.Style;
import android.os.Handler;
import android.os.Message;
import android.util.AttributeSet;
import android.view.WindowManager;
import android.widget.TextView;
import com.kaixin001.item.LinkInfo;
import com.kaixin001.model.StateFaceModel;
public class ScrollText extends TextView {
public static final int TEXT_TIMER = 100;
private float textLength = 0f;// 文本长度
private float viewWidth = 0f;
private float step = 0f;// 文字的横坐标
private float y = 0f;// 文字的纵坐标
private float temp_view_plus_text_length = 0.0f;// 用于计算的临时变量
private float temp_view_plus_two_text_length = 0.0f;// 用于计算的临时变量
private boolean isStarting = false;// 是否开始滚动
private int left = 0;
private int right = 0;
private Paint paint = null;// 绘图样式
private String text = "";// 文本内容
private Bitmap txtBmp;
private Canvas txtCanvas;
private FontMetrics fontMetrics;
private Timer timer = new Timer();
private ArrayList<LinkInfo> stateList;
Handler handler;
TimerTask task = new TimerTask() {
public void run() {
if (handler != null && isStarting) {
Message msg = Message.obtain();
msg.what = TEXT_TIMER;
handler.sendMessage(msg);
}
}
};
public ScrollText(Context context) {
super(context);
}
public ScrollText(Context context, AttributeSet attrs) {
super(context, attrs);
}
public ScrollText(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
/** */
/**
* 文本初始化,每次更改文本内容或者文本效果等之后都需要重新初始化一下
*/
public void init(WindowManager windowManager, Handler handler) {
try {
this.handler = handler;
paint = new Paint();
paint.setAntiAlias(true);
paint.setStyle(Style.STROKE);
paint.setTextSize(getTextSize());
paint.setColor(getCurrentTextColor());
text = getText().toString();
textLength = 0;
// textLength = paint.measureText(text);
int len = stateList.size();
for (int i = 0; i < len; i++) {
LinkInfo info = stateList.get(i);
if (info.isFace()) {// 表情符排版
Bitmap faceBmp = StateFaceModel.getInstance()
.getSmallFaceIcon(info.getContent());
int xLen = faceBmp.getWidth();
textLength += xLen + 4;
continue;
}
String strContent = info.getContent();
float xLen = paint.measureText(strContent);
textLength += xLen;
}
left = this.getPaddingLeft();
right = this.getPaddingRight();
step = textLength;
fontMetrics = paint.getFontMetrics();
y = getPaddingTop();// getTextSize() + getPaddingTop();
txtBmp = null;
} catch (Exception ex) {
ex.printStackTrace();
}
}
public void scrollText() {
if (!isStarting) {
return;
}
invalidate();
if (viewWidth < textLength) {
step += 0.5;
if (step > temp_view_plus_two_text_length) {
step = textLength;
}
}
}
public void setStateList(ArrayList<LinkInfo> stateList) {
this.stateList = stateList;
}
private void setTxtBmp() {
if (txtBmp == null && fontMetrics != null) {
y = -paint.ascent();// fontMetrics.bottom -
// fontMetrics.ascent;//(this.getHeight() -
// (int)fontMetrics.ascent)/2;
viewWidth = getWidth() - left - right;
temp_view_plus_text_length = viewWidth + textLength;
temp_view_plus_two_text_length = viewWidth + textLength * 2;
txtCanvas = new Canvas();
int width = (int) viewWidth;
float height = getHeight();
txtBmp = Bitmap.createBitmap(width, (int) height, Config.ARGB_8888);
txtCanvas.setBitmap(txtBmp);
}
}
/** */
/**
* 开始滚动
*/
public void startScroll() {
isStarting = true;
}
/** */
/**
* 停止滚动
*/
public void stopScroll() {
isStarting = false;
// invalidate();
}
public void start() {
timer.schedule(task, 10, 20);
}
public void stop() {
timer.cancel();
}
@Override
public void onDraw(Canvas canvas) {
try {
setTxtBmp();
if (txtBmp == null) {
return;
}
Paint txtPaint = new Paint();
txtPaint.setColor(Color.WHITE);
txtPaint.setStyle(Style.FILL);
txtCanvas.drawRect(0, 0, txtBmp.getWidth(), txtBmp.getHeight(),
txtPaint);
txtPaint.setAntiAlias(true);
txtPaint.setStyle(Style.STROKE);
txtPaint.setTextSize(getTextSize());
txtPaint.setColor(getCurrentTextColor());
float x = 0;
// step为text的宽度
if (viewWidth < textLength) {
x = temp_view_plus_text_length - step;
}
int len = stateList.size();
float curLen = x;
for (int i = 0; i < len; i++) {
LinkInfo info = stateList.get(i);
if (info.isFace()) {// 表情符排版
Bitmap faceBmp = StateFaceModel.getInstance()
.getSmallFaceIcon(info.getContent());
int xLen = faceBmp.getWidth();
txtCanvas.drawBitmap(faceBmp, curLen + 2, 0, txtPaint);
curLen += xLen + 4;
continue;
}
String strContent = info.getContent();
strContent = strContent.replaceAll("n", " ");
float xLen = txtPaint.measureText(strContent);
//因为x的值一直减少,所以文字可以滚动
txtCanvas.drawText(strContent, curLen, y, txtPaint); //<span style="font-family: Arial, Helvetica, sans-serif;">txtCanvas早drawtext.txtBmp上也自然也带有这些text</span>
curLen += xLen;
}
canvas.drawBitmap(txtBmp, left, 0, paint);
} catch (Exception ex) {
ex.printStackTrace();
}
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
txtBmp = null;
setTxtBmp();
}
}