利用之前寫的顏色混合原理,在實現了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;
}
/* argb按透明度作爲比例轉yuv,大小端問題把R和B通道倒置*/
float alpha = (bmpData[k] >> 24 & 0xFF);
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 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的透明圖混合在一起了。