Průkopnická firma FingerWorks kolem roku 2000 vyvinula vícedotykové trackpady s gesty a klávesnice jako TouchStream LP. V roce 2005 ji koupil Apple, výrobu těchto produktů ukončil a dotykové technologie využil při vývoji iPhone. Multiplatformní projekt Apple Magic TouchstreamLP nyní implementuje funkcionalitu TouchStream LP na současném Apple Magic Trackpad, resp. jejich dvojici. Diskuze k vydání probíhá na Redditu.
Byla vydána nová verze 10.3 sady aplikací pro SSH komunikaci OpenSSH. Přináší řadu bezpečnostních oprav, vylepšení funkcí a oprav chyb.
Cloudflare představil open source redakční systém EmDash. Jedná se o moderní náhradu WordPressu, která řeší bezpečnost pluginů. Administrátorské rozhraní lze vyzkoušet na EmDash Playground.
Bratislava OpenCamp 2026 zverejnil program a spustil registráciu. Štvrtý ročník komunitnej konferencie o otvorených technológiách prinesie 19 prednášok na rôzne technologické témy. Konferencia sa uskutoční v sobotu 25. apríla 2026 v priestoroch FIIT STU v Bratislave.
Na iVysílání lze zhlédnout všechny díly kultovního sci-fi seriálu Červený trpaslík.
Společnost Valve aktualizovala přehled o hardwarovém a softwarovém vybavení uživatelů služby Steam. Podíl uživatelů Linuxu dosáhl v březnu 5,33 % (Windows -4,28 %, OSX +1,19 %, Linux +3,10 %). Nejčastěji používané linuxové distribuce jsou Arch Linux, Linux Mint a Ubuntu. Při výběru jenom Linuxu vede SteamOS Holo s 24,48 %. Procesor AMD používá 67,48 % hráčů na Linuxu.
Společnost Apple slaví padesáté narozeniny. Založena byla 1. dubna 1976.
FreeTube, desktopový klient pro YouTube využívající lokální API, byl vydán ve verzi 0.24.0. Toto velké opravné vydání implementuje SABR (Server-Based Adaptive Bit Rate), což řeší část nedávných problémů s načítáním videí z YouTube, a aktualizuje základní komponenty jako Electron nebo přehrávač Shaka Player.
Je tu opět apríl. O víkendu zmizel kamion s 12 tunami tyčinek KitKat. Firmy to využívají k aprílovým žertům. Groupon má super akci. Koupíte 1 tyčinku a dostanete 100 zdarma. Ryanair si přelepil letadla. Šéf Outlooku se ptá, proč mají v baráku 14 beden tyčinek KitKat (𝕏). Prusa Research představuje Prusa Pro ACU a vysvětluje proč přílišné sušení škodí vaším filamentům. Telefon Sony Xperia má miliónnásobný zoom (𝕏). PC.net představil Super Ultrabox 2600 se zajímavými parametry. Další aprílové novinky například na April Fools' Day On The Web.
Společnost OpenAI, která stojí za chatovacím robotem s umělou inteligencí (AI) ChatGPT, získala od investorů 122 miliard USD (2,6 bilionu Kč). Hodnota společnosti tak dosáhla 852 miliard dolarů (více než 18 bilionů Kč). Nejnovější kolo investování se stalo největší, jaké zatím firma uskutečnila, a peníze mají posílit ambiciózní plány rozšíření výpočetní kapacity, datových center a nábor talentů.
#define RGB_TO_YCbCrJPEG_Y(Rd, Gd, Bd) \
( + ((306 * (Rd) + 601 * (Gd) + 117 * (Bd)) >> 10))
#define RGB_TO_YCbCrJPEG_Cb(Rd, Gd, Bd) \
(128 - ((173 * (Rd) + 339 * (Gd) - 512 * (Bd)) >> 10))
#define RGB_TO_YCbCrJPEG_Cr(Rd, Gd, Bd) \
(128 + ((512 * (Rd) - 429 * (Gd) - 83 * (Bd)) >> 10))
void ycbcr_bgr_to_jpeg420(save_thread_t *th, unsigned char *from)
{
unsigned int Ypix;
unsigned int op1, op2, op3, op4;
unsigned char Rd, Gd, Bd;
unsigned int ox, oy, Yy, Yx;
unsigned char *Y, *Cb, *Cr;
Y = th->yuv_buffer;
Cb = &th->yuv_buffer[th->yw * th->yh];
Cr = &th->yuv_buffer[th->yw * th->yh + th->cw * th->ch];
oy = (th->yh - 2) * th->stride;
ox = 0;
for (Yy = 0; Yy < th->yh; Yy += 2) {
for (Yx = 0; Yx < th->yw; Yx += 2) {
op1 = ox + oy;
op2 = op1 + 4;
op3 = op1 + th->stride;
op4 = op2 + th->stride;
Rd = (from[op1 + 2] + from[op2 + 2] + from[op3 + 2] + from[op4 + 2]) >> 2;
Gd = (from[op1 + 1] + from[op2 + 1] + from[op3 + 1] + from[op4 + 1]) >> 2;
Bd = (from[op1 + 0] + from[op2 + 0] + from[op3 + 0] + from[op4 + 0]) >> 2;
/* CbCr */
*Cb++ = RGB_TO_YCbCrJPEG_Cb(Rd, Gd, Bd);
*Cr++ = RGB_TO_YCbCrJPEG_Cr(Rd, Gd, Bd);
/* Y' */
Ypix = Yx + Yy * th->yw;
Y[Ypix] = RGB_TO_YCbCrJPEG_Y(from[op3 + 2],
from[op3 + 1],
from[op3 + 0]);
Y[Ypix + 1] = RGB_TO_YCbCrJPEG_Y(from[op4 + 2],
from[op4 + 1],
from[op4 + 0]);
Y[Ypix + th->yw] = RGB_TO_YCbCrJPEG_Y(from[op1 + 2],
from[op1 + 1],
from[op1 + 0]);
Y[Ypix + 1 + th->yw] = RGB_TO_YCbCrJPEG_Y(from[op2 + 2],
from[op2 + 1],
from[op2 + 0]);
ox += 4 * 2;
}
ox = 0;
oy -= 2 * th->stride;
}
}
Šoupnul jsem to do cyklu s počtem iterací 30*20 (tj. 20s video při 30 snímcích za sekundu), rozlišení 1280x1024. Konverze je hotová za 6.243s.
Zdálo se mi to dlouho, tak jsem zkoušel optimalizovat a upatlal jsem toto:
void ycbcr_bgr_to_jpeg420_mine(save_thread_t *th, unsigned char *from)
{
unsigned bytes_per_row;
unsigned extra_per_row;
unsigned char *row;
unsigned char *end_pix;
unsigned char *end_row;
unsigned char *Y, *Cb, *Cr;
unsigned char R, G, B;
Y = th->yuv_buffer + (th->yh - 2) * th->yw;
Cb = &th->yuv_buffer[th->yw * th->yh + (th->ch - 1) * th->cw];
Cr = &th->yuv_buffer[th->yw * th->yh + th->cw * th->ch + (th->ch - 1) * th->cw];
bytes_per_row = th->width * 4;
extra_per_row = th->stride - bytes_per_row;
end_row = from + th->stride * th->height;
for (; from != end_row; from += th->stride + extra_per_row)
{
end_pix = from + bytes_per_row;
for (; from != end_pix; from += 2*4)
{
B = (from[0 + 0] + from[4 + 0] + from[th->stride + 0] + from[th->stride + 4 + 0]) >> 2;
G = (from[0 + 1] + from[4 + 1] + from[th->stride + 1] + from[th->stride + 4 + 1]) >> 2;
R = (from[0 + 2] + from[4 + 2] + from[th->stride + 2] + from[th->stride + 4 + 2]) >> 2;
*Cb = RGB_TO_YCbCrJPEG_Cb(R, G, B);
*Cr = RGB_TO_YCbCrJPEG_Cr(R, G, B);
Y[0] = RGB_TO_YCbCrJPEG_Y(from[th->stride + 2], from[th->stride + 1], from[th->stride + 0]);
Y[1] = RGB_TO_YCbCrJPEG_Y(from[th->stride + 4 + 2], from[th->stride + 4 + 1], from[th->stride + 4 + 0]);
Y[th->yw + 0] = RGB_TO_YCbCrJPEG_Y(from[0 + 2], from[0 + 1], from[0 + 0]);
Y[th->yw + 1] = RGB_TO_YCbCrJPEG_Y(from[4 + 2], from[4 + 1], from[4 + 0]);
Y += 2;
++Cb;
++Cr;
}
Y -= th->yw;
Y -= th->yw;
Y -= th->yw;
Cb -= th->cw;
Cb -= th->cw;
Cr -= th->cw;
Cr -= th->cw;
}
}
Prohodil jsem směr (btw řádky vrácené z opengl jsou v rgb bufferu opačně (horní řádek dole)), čtu odshora dolů a zapisuju odspoda nahoru. No nevím jestli to zlepšilo to, nebo jestli to zlepšilo použítí pointerů na některých místech, každopádně dostal jsem se na 4.854s.
Ale protože Jardíkovi se to pořád zdálo hodně a ten obraz se zdál být tmavý, tak jsem našel jiný vzorec na převod a zkoušel jsem to spatlat ... v assembleru.
global bgr_to_jpeg420_sse ; Y = ( ( 66 * R + 129 * G + 25 * B + 128) >> 8) + 16 ; U = ( ( -38 * R - 74 * G + 112 * B + 128) >> 8) + 128 ; V = ( ( 112 * R - 94 * G - 18 * B + 128) >> 8) + 128 section .data align=16 vec_alpha_1_and: align 16 dd 0x00FFFFFF, 0x00FFFFFF, 0x00FFFFFF, 0x00FFFFFF vec_alpha_1_or: align 16 dd 0x01000000, 0x01000000, 0x01000000, 0x01000000 vec_dd_16: align 16 dd 16, 16, 16, 16 vec_dd_128: align 16 dd 128, 128, 128, 128 vec_y_const: align 16 dw 25, 129, 66, 128, 25, 129, 66, 128 vec_u_const: align 16 dw 112, -74, -38, 128, 112, -74, -38, 128 vec_v_const: align 16 dw -18, -94, 112, 128, -18, -94, 112, 128 section .text ; this expects these xmm registers to be set: ; xmm4 to vec_alpha_1_and ; xmm5 to vec_alpha_1_or ; xmm6 to vec_y_const ; xmm7 to vec_dd_16 ; input vector in xmm0 as (argb3, argb2, argb1, argb0) ; returns result as dwords in xmm1 (y3,y2,y1,y0) ; destroys content of xmm0, xmm2 %macro bgr_to_jpeg420_sse_get_Y 0 pand xmm0, xmm4 ; set alpha of each pixel to 1 por xmm0, xmm5 movaps xmm1, xmm0 ; copy to xmm1 pxor xmm2, xmm2 ; zero xmm2 punpckhbw xmm0, xmm2 ; interleave xmm2 with first 2 pixels, xmm = (a3,r3,g3,b3,a2,r2,g2,b2) punpcklbw xmm1, xmm2 ; interleave xmm2 with second 2 pixels, xmm = (a1,r1,g1,b1,a0,r0,g0,b0) pmaddwd xmm0, xmm6 ; xmm = (128a3+66r3 = A3,129g3+25b3 = B3,128a2+66r2 = A2,129g2+25b2 = B2) pmaddwd xmm1, xmm6 ; xmm = (128a1+66r1 = A1,129g1+25b1 = B1,128a0+66r0 = A0,129g0+25b0 = B0) pshufd xmm0, xmm0, 0b11011000 ; (A3, A2, B3, B2) pshufd xmm1, xmm1, 0b11011000 ; (A1, A0, B1, B0) movaps xmm2, xmm1 ; save xmm1 punpckhqdq xmm1, xmm0 ; xmm1 = (A3, A2, A1, A0) punpcklqdq xmm2, xmm0 ; xmm2 = (B3, B2, B1, B0) paddd xmm1, xmm2 ; xmm1 = (A3+B3, A2+B2, A1+B1, A0+B0) psrld xmm1, 8 ; xmm1 /= 256 paddd xmm1, xmm7 ; xmm1 += 16 ; xmm1 = ( y3, y2, y1, y0) %endmacro ; void bgr_to_jpeg420_sse( ; unsigned char *yuv_buffer, // rdi, must be 16B aligned ; const unsigned char *bgr_buffer, // rsi, must be 16B aligned ; unsigned int width, // rdx, must be multiple of 4 ; unsigned int height // rcx, must be multiple of 2 ; ) bgr_to_jpeg420_sse: push rax push rbx push rcx push rdx push rsi push rdi push rbp mov rbx, rdx ; rbx = width, mul will overwrite rdx later mov rax, rdx mul rcx ; rax = width * height mov rdx, rbx ; move back width to rdx mov rbp, rdi add rbp, rax ; rbp = pointer to U part mov rbx, rax shr rbx, 2 ; rbp+rbx = pointer to V part shl rax, 2 ; rax = rgb buffer size (width * height * 4) mov r9, rdx ; r9 = width shl r9, 2 ; r9 = width*4 = rgb_buffer stride add rsi, rax ; make rsi point to the last but one row sub rsi, r9 ; of rgb_buffer sub rsi, r9 shr rcx, 1 ; we process 2 rows in one loop,so make height half movaps xmm4, [rel vec_alpha_1_and] movaps xmm5, [rel vec_alpha_1_or] movaps xmm6, [rel vec_y_const] movaps xmm7, [rel vec_dd_16] movaps xmm3, [rel vec_dd_128] movaps xmm10, [rel vec_u_const] movaps xmm11, [rel vec_v_const] .__outer_loop: mov r8, rdx ; r8 = width shr r8, 2 ; r8 = width / 4 (4 pixels in one loop) .__inner_loop: movaps xmm8, [rsi] ; load the 4 pixels in first row, xmm = (argb3,argb2,argb1,argb0) movaps xmm9, [rsi+r9] ; load the 4 pixels in second row, xmm = (argb3,argb2,argb1,argb0) movaps xmm0, xmm9 ; get Y value for each pixel in the vector bgr_to_jpeg420_sse_get_Y ; xmm1 = ( y3, y2, y1, y0) pextrw eax, xmm1, 0 ; store it in yuv_buffer mov BYTE [rdi], al pextrw eax, xmm1, 2 mov BYTE [rdi+1], al pextrw eax, xmm1, 4 mov BYTE [rdi+2], al pextrw eax, xmm1, 6 mov BYTE [rdi+3], al movaps xmm0, xmm8 ; get Y value for each pixel in the vector bgr_to_jpeg420_sse_get_Y ; xmm1 = ( y3, y2, y1, y0) pextrw eax, xmm1, 0 ; store it in yuv_buffer mov BYTE [rdi+rdx], al pextrw eax, xmm1, 2 mov BYTE [rdi+rdx+1], al pextrw eax, xmm1, 4 mov BYTE [rdi+rdx+2], al pextrw eax, xmm1, 6 mov BYTE [rdi+rdx+3], al ; Calc U and V here movaps xmm0, xmm8 pavgb xmm0, xmm9 pshufd xmm1, xmm0, 0b10000000 pshufd xmm0, xmm0, 0b11010000 pavgb xmm0, xmm1 pand xmm0, xmm4 ; set alpha of each pixel to 1 por xmm0, xmm5 pxor xmm2, xmm2 punpckhbw xmm0, xmm2 ; xmm0 now contains words (a1,r1,g1,b1,a0,r0,g0,b0) movaps xmm8, xmm0 ; save it to xmm8 as U calculation will overwrite xmm0 ; calc U pmaddwd xmm0, xmm10 ; xmm = (128a1-38r1 = A1,-74g1+112b1 = B1,128a0-38r0 = A0,-74g0+112b0 = B0) pshufd xmm1, xmm0, 0b10110001 paddd xmm0, xmm1 ; xmm = (A1+B1, A1+B1, A0+B0, A0+B0) psrld xmm0, 8 ; xmm /= 256 paddd xmm0, xmm3 ; xmm += 128 pextrw eax, xmm0, 0 mov BYTE [rbp], al pextrw eax, xmm0, 4 mov BYTE [rbp+1], al ; calc V movaps xmm0, xmm8 pmaddwd xmm0, xmm11 ; xmm = (128a1-38r1 = A1,-74g1+112b1 = B1,128a0-38r0 = A0,-74g0+112b0 = B0) pshufd xmm1, xmm0, 0b10110001 paddd xmm0, xmm1 ; xmm = (A1+B1, A1+B1, A0+B0, A0+B0) psrld xmm0, 8 ; xmm /= 256 paddd xmm0, xmm3 ; xmm += 128 pextrw eax, xmm0, 0 mov BYTE [rbp+rbx], al pextrw eax, xmm0, 4 mov BYTE [rbp+rbx+1], al add rbp, 2 add rsi, 16 ; rgb_buffer += 16 add rdi, 4 ; Y += 4 dec r8 ; remaining iterations for inner loop jnz .__inner_loop add rdi, rdx ; skip the row sub rsi, r9 ; make rsi point to the 2 previous rows sub rsi, r9 sub rsi, r9 dec rcx ; decrement number of remaining rows jnz .__outer_loop ; if not zero, continue .__end: pop rbp pop rdi pop rsi pop rdx pop rcx pop rbx pop rax ret
S tímto jsem se dostal na 3.509s. Kód je docela ošklivý a patlal jsem to s instrukcema, co jsem našel v různých seznamech instrukcí po netu se válících. No a proč to sem házím ... kdyby se našel nějaký expert co by to rád zoptimalizoval, nebo poradil, kde a co tam zlepšit, .. byl bych moc vděčný.
Používám tam x86-64 registry, s xmm registry jsem se prostě nevešel a když ty konstanty nebudu mít přednačtené a budu je pak z paměti načítat v cyklu, tak to zdržuje a algoritmus to zpomalí. A když už jsem se nevešel s xmm registry, tak už jsem rovnou použil i x86-64 GPR.
Tiskni
Sdílej:
Kromě šumu je analogová technika ve všech vlastnostech o mnoho řádů napřed. To co se dá v analogové technice vyřešit součástkami za 10 Kč, na vyřešení stejné operace v digitální technice je potřeba stroj za sto tisíc dolarů a ještě bude pokulhávat za analogem.Pokud si vyberete jednu z těch několika málo operací, které se dají analogově snadno realizovat, tak ano
Ale rád se nechám přesvědčit – stačí, když ukážete, jak jednoduše analogově spočítat RSA
Mým výsledkem má být yuv420, pro každé 4 pixely (2x2) je tam jeden U a jeden V. Takže když mám pro 4 pixely už 4 Y', tak abych mohl použít ten vzorec řekněme pro U, musel bych nejdříve zprůměrovat B hodnoty pro ty 4 pixely, zprůměrovat Y hodnoty pro ty pixely a použít vzorec.
Momentálně to dělám tak, pro každý pixel vypočítám Y, zprůměruju rgb hodnoty pro 2x2 sousední pixely a spočtu U. Pravděpodobně by mi použítí "jednodušší" vzorce v tomto případě moc nepomohlo. Leda bych U nepočítal z průměrů ale pouze z jednoho pixelu, ale pak bych asi nedostal hezký výsledek.
Jak jsem psal, nerozumím tomu do detailu. Ale jednu věc vím zcela určitě. Volba lepšího algoritmu a pohrání si s matematikou dává mnohem větší zrychlení, než ďábelsky kódovat nanosekundy pomocí asm. Možná se ukáže, že to není k ničemu.Jenže Jardík obvykle jde cestou předčasné optimalizace a ukazuje nám proč se nemá dělat. (viz. jeho starší blog kde ďábelsky optimalizoval v ASM, až se dostal na stejný čas jako naivní C program)
>> 2" u onoho supsamplingu se mi také moc nelíbí, protože to se může bez ztráty kytičky udělat až na konci a získámé tím trochu vyšší přenost.
No a ještě bych nejspíš držel dva ukazatele do RGB pole from, pošouplé o th->stride a jenom je inkrementoval, protože i když z toho překladač udělá nepřímé adresování, stále to bude sahat do paměti. To samé i u výsledného Y a th->yw.