利用之前写的颜色混合原理,在实现了ARGB转成YUV后,通过alpha值按比例混合原有的YUV值即可:
具体实现代码:
fastYuvInsertBmp.c
过程中我使用了一个循环做了个假的三色bmp,你可以用真的bmp代替进行测试。精华在那个双重循环里面,留个坑以后解释位置偏移原理:
#include "stdio.h"
#include "stdlib.h"
#include "multiThread.c"
#include <sys/time.h>
struct params
{
char* yuvData;
int* bmpData;
int yuvW;
int yuvH;
int bmpH;
int bmpW;
int top;
int left;
float startRation;
float endRation;
};
typedef struct params Params;
struct timeval stamp;
void yuvInsert(char* yuvData, int* bmpData, int yuvW, int yuvH, int bmpH, int bmpW, int top, int left);
void* yuvInsertProcess(void *params);
int main(int argc, char* argv[]) {
FILE *f = fopen("/media/chenjiezhu/work2/其他/yuv_argb_mix/test/getSourceYuv/1592896365132_4672x2128_NV21.NV21", "rb");
FILE *f2 = fopen("/media/chenjiezhu/work2/其他/yuv_argb_mix/test/getSourceYuv/1592896365132_4672x2128_NV21_change.NV21", "wb+");
int yuvW = 4672;
int yuvH = 2128;
int bmpW = 500;
int bmpH = 500;
int *bmpData = (int*) malloc(sizeof(int) * bmpW * bmpH);
char *yuvData = (char*) malloc(sizeof(char) * yuvW * yuvH * 1.5f);
printf("the f is %d\n", f);
int i;
for(i = 0; i < bmpW * bmpH; i++) {
if(i > bmpW * (int)(bmpH * 2 / 3.0f)) {
bmpData[i] = ((100 << 24) | (0xFF << 16));
continue;
}
if(i > bmpW * (int)(bmpH * 1 / 3.0f)) {
bmpData[i] = ((100 << 24) | (0xF0 << 8));
continue;
}
if(i > 0) {
bmpData[i] = ((100 << 24) | (0xFF));
continue;
}
}
fread(yuvData, 1, yuvW * yuvH * 1.5f, f);
fclose(f);
yuvInsert(yuvData, bmpData, yuvW, yuvH, bmpW, bmpH, 500, 500);
mySleep(1000 * 1000);
printf("运行结束\n");
fwrite(yuvData, 1, yuvW * yuvH * 1.5f, f2);
fclose(f2);
return 0;
}
/*top,left用于位图写到yuv图像的哪个坐标作为左上角用的 */
void yuvInsert (char* yuvData, int* bmpData, int yuvW, int yuvH, int bmpW, int bmpH, int top, int left) {
//yuvInsertProcess(yuvData, bmpData, yuvW, yuvH, bmpH, bmpW, top, left, 0.0f, 1.0f);
int threadCount = 4;
threadFinishedCount = 0;
struct timeval stamp;
ThreadFun funArr[threadCount];
int i;
for(i = 0; i < threadCount; i++) {
Params *params = (Params*) malloc(sizeof(Params));
params->bmpData = bmpData;
params->bmpH = bmpH;
params->bmpW = bmpW;
params->startRation = i / (threadCount * 1.0f);
params->endRation = (i + 1) / (threadCount * 1.0f);
params->left = left;
params->top = top;
params->yuvData = yuvData;
params->yuvH = yuvH;
params->yuvW = yuvW;
funArr[i].fun = yuvInsertProcess;
funArr[i].params = params;
}
gettimeofday(&stamp, NULL);
long startTime = stamp.tv_sec * 1000000 + stamp.tv_usec;
startThread(threadCount, funArr);
// for(i = 0; i < threadCount; i++) {
// funArr[i].fun(funArr[i].params);
// }
while (threadFinishedCount < threadCount) {
mySleep(1);
}
gettimeofday(&stamp, NULL);
long endTime = stamp.tv_sec * 1000000 + stamp.tv_usec;
printf("finish time: %ld ms\n", (endTime - startTime) / 1000);
LOGI("finish time: %ld ms\n", (endTime - startTime) / 1000);
}
void* yuvInsertProcess (void *params) {
Params *p = (Params*) params;
int *bmpData = p->bmpData;
char *yuvData = p->yuvData;
int yuvW = p->yuvW;
int yuvH = p->yuvH;
int bmpW = p->bmpW;
int bmpH = p->bmpH;
int area = yuvW * yuvH;
int bmpArea = bmpW * bmpH;
float startRation = p->startRation;
float endRation = p->endRation;
int left = p->left;
int top = p->top;
/*i : 每行起始位置
j : 位图游标,定位读位图的哪个位置的像素
count : 了解已经操作bmp和yuv的第几行
k : 读bmp并转换到yuv的内循环游标
l : 内循环(行遍历循环)游标*/
int i, j, k, l, count;
LOGI("startTrans, startRation:%f, endRation:%f\n", startRation, endRation);
//每次读一行yuv和一行bmp
int yuvStartPos = (top + (int)(bmpH * startRation)) * yuvW + left; //800000 + 500 + 960000
// int yuvStartPos = 1760500;
int bmpStartPos = 0 + bmpW * (int)(bmpH * startRation); //0 + 125000
// int bmpStartPos = 125000;
for(i = yuvStartPos, j = bmpStartPos, count = 1 + startRation * bmpH;
i < yuvStartPos + yuvW * /*(int)*/ (yuvH * (endRation - startRation)) && j < bmpStartPos + bmpW * /*(int)*/ (bmpH * (endRation - startRation));
i += yuvW, j += bmpW, count++)
{
//printf("i = %d, j = %d, count = %d\n", i, j, count);
for(k = j, l = 0; k < bmpW * count; k++, l++) { //count 行号
if (k > bmpW * bmpH) {
break;
}
if (left + l >= yuvW || top + count > yuvH) { //防止横向像素越界、纵向像素越界。原理,左边起点 + bmp横向游标不能大于yuvw,上边起点 + bmp已读行数不能大于yuvh
break;
}
if (left + l < 0 || (top + (count - 1) < 0)) continue; //坐标小于0则不必处理
/* argb按透明度作为比例转yuv,大小端问题把R和B通道倒置*/
int alpha = (bmpData[k] >> 24 & 0xFF);
if (alpha == 0) {
continue;
}
float ratio = alpha / 255.0f;
float rationReverse = (255 - alpha) / 255.0f;
unsigned int R = (bmpData[k] & 0xFF);
unsigned int G = (bmpData[k] >> 8 & 0xFF);
unsigned int B = (bmpData[k] >> 16 & 0xFF);
unsigned char argbToY = (unsigned char) ((77 * R + 150 * G + 29 * B) >> 8);
// unsigned char argbToY = (unsigned char) ((66 * R + 129 * G + 25 * B) >> 8 + 16);
argbToY < 0 ? 0 : ((argbToY > 255) ? 255 : argbToY);
/* yuv按透明度作为比例和刚刚转好的新yuv值混合*/
unsigned char oldY = yuvData[i + l];
yuvData[i + l] = argbToY * ratio + oldY * rationReverse; //新的Y值
if ((count - 1) % 2 == 0 && l % 2 == 0) { //隔行隔列写入uv数据
int pos = yuvW * yuvH + ((count - 1) / 2 + top / 2) * yuvW + left + l;
// unsigned char argbToU = (unsigned char) (((-44 * R - 87 * G + 131 * B) >> 8) + 128);
// unsigned char argbToV = (unsigned char) (((131 * R - 110 * G - 21 * B) >> 8) + 128);
unsigned char argbToU = (unsigned char) ((( -38 * R - 74 * G + 112 * B + 128) >> 8) + 128);
unsigned char argbToV = (unsigned char) ((( 112 * R - 94 * G - 18 * B + 128) >> 8) + 128);
unsigned char oldV = yuvData[pos];
unsigned char oldU = yuvData[pos + 1];
int newV = argbToV * ratio + oldV * rationReverse;//新的V值
int newU = argbToU * ratio + oldU * rationReverse;//新的U值
// yuvData[pos] = (newV < 0) ? 0 : ((newV > 255) ? 255 : newV);
// yuvData[pos + 1] = (newU < 0) ? 0 : ((newU > 255) ? 255 : newU);
yuvData[pos] = newV;
yuvData[pos + 1] = newU;
//printf("old value: %d, new value argbToY :%d \t", old, argbToY);
}
}
}
free(params);
threadFinishedCount ++;
}
多线程工具multiThread.c:
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
#include <time.h>
#ifdef _WIN32
#include <windows.h>
#include <direct.h>
#include <io.h>
#include <process.h>
#else
#include <unistd.h>
#include <getopt.h>
#include <sys/types.h>
#include <pthread.h>
#endif
#define new(Class) (Class*)malloc(sizeof(Class))
typedef struct ThreadFun ThreadFun;
struct ThreadFun{
void* params; //线程函数的参数
void* (*fun)(void *params); //线程函数指针
};
void mySleep(long ms){
#ifdef _WIN32
Sleep(ms);
#else
usleep(ms);
#endif
}
void startThread(int threadNum, ThreadFun funArray[]){
#ifdef _WIN32
HANDLE handle[threadNum];
int i = 0;
for(i = 0; i < threadNum; i++){
handle[i] = (HANDLE) _beginthreadex(NULL, 0, funArray[i].fun, funArray[i].params, 0, NULL); // create thread ok
}
//DWORD dwThread;
//HANDLE handle = CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)fun,(LPVOID)NULL,0,&dwThread); // create thread ok
printf("Win32\n");
//WaitForMultipleObjects(threadNum, handle, TRUE, INFINITE); //等待这几个线程结束
for(i = 0; i < threadNum; i++){
CloseHandle(handle[i]); //关闭句柄
}
#else
pthread_mutex_t mutex;
pthread_cond_t cond;
pthread_t pt[threadNum];
int i = 0;
pthread_mutex_init(&mutex, NULL);
pthread_cond_init(&cond, NULL);
for(i = 0; i < threadNum; i++){
pthread_create(&pt[i], NULL, funArray[i].fun, funArray[i].params);
}
// pthread_exit(0); //等待全部线程结束函数才结束
#endif
}
/*void shit(void *p){
while(1) {
printf("%s\n", (char*) p);
mySleep(1);
}
}
void* shit2(void *p){
while(1) {
printf("%s\n", (char*) p);
mySleep(1);
}
}
int main(){
ThreadFun funArr[2];
funArr[0].fun = shit;
funArr[0].params = "asdasd";
funArr[1].fun = shit2;
funArr[1].params = "111232323";
startThread(2, funArr);
while(1){
printf("shit\n");
mySleep(1);
}
return 0;
}*/
实现效果:
可以看到,yuv图片和我的bmp图片按照bmp的透明图混合在一起了。