From ae6cb421a95369915ff451cc263a2f5e3f9c7ea0 Mon Sep 17 00:00:00 2001 From: Chris Marchesi Date: Wed, 20 Nov 2024 09:58:59 -0800 Subject: [PATCH] export_png: allow exporting of alpha surfaces to greyscale This allows the support of exporting of alpha8 pixel surfaces to greyscale. This is in line with Cairo. A pretty simple change actually, given that we have most of the other things in place for PNG export, we just add it as a supported surface type and handle it in header and pixel encoding. --- spec/047_fill_triangle_alpha_gray.zig | 7 ++-- ...047_fill_triangle_alpha_gray_pixelated.png | Bin 3192 -> 1181 bytes .../047_fill_triangle_alpha_gray_smooth.png | Bin 3442 -> 1561 bytes src/export_png.zig | 30 ++++++++++++------ 4 files changed, 25 insertions(+), 12 deletions(-) diff --git a/spec/047_fill_triangle_alpha_gray.zig b/spec/047_fill_triangle_alpha_gray.zig index dcdc45b..9f03e57 100644 --- a/spec/047_fill_triangle_alpha_gray.zig +++ b/spec/047_fill_triangle_alpha_gray.zig @@ -1,7 +1,8 @@ // SPDX-License-Identifier: 0BSD // Copyright © 2024 Chris Marchesi -//! Case: Renders and fills a triangle on a 300x300 surface. +//! Case: Renders and fills a triangle on a 300x300 surface using alpha8 for +//! grayscale. //! //! This is similar to the 003_fill_triangle.zig, but uses alpha8 as its source //! versus RGB. Also renders a gray triangle at half alpha to test optimized @@ -15,8 +16,8 @@ pub const filename = "047_fill_triangle_alpha_gray"; pub fn render(alloc: mem.Allocator, aa_mode: z2d.options.AntiAliasMode) !z2d.Surface { const width = 300; const height = 300; - var sfc = try z2d.Surface.initPixel( - .{ .rgb = .{ .r = 0xFF, .g = 0xFF, .b = 0xFF } }, // White so that srcOver shows up correctly + var sfc = try z2d.Surface.init( + .image_surface_alpha8, alloc, width, height, diff --git a/spec/files/047_fill_triangle_alpha_gray_pixelated.png b/spec/files/047_fill_triangle_alpha_gray_pixelated.png index 096ce634d0423b3e9adf5d7279e19d31d1c5ae33..c7cb03d804f8f65a67859d6bf544a31b1879bee9 100644 GIT binary patch literal 1181 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 diff --git a/spec/files/047_fill_triangle_alpha_gray_smooth.png b/spec/files/047_fill_triangle_alpha_gray_smooth.png index 0311be246ed6ba11d65910c0cf0ff8140f0ab0b9..2d538ace7aa78cfa74534f1f7401979fae388a17 100644 GIT binary patch literal 1561 zcmaKsdr%Ws6o)tAA+QOr!dfZCf=^(V0-7o)8jvWM${;4ox`O5*HIULON>Lo-p-><^ zDndrBj)Vab$V$L$3)I#iV5R6Z1hR<3w8%)U4ftS+Rx74g^zZ)h&D^=OGv7JqckY(N z$8F&{c{xE4#Ep%KPJkd5`UVGZ0VN@WO%P-^9vdAgI(%-*G{C*ica4tRH(~U4YC70{ z_1c+Hm|dUkrYAGKtCq%Ay>7QObSiV07t_fbM@yKmSg0Ns=!|D!sfM?(CrXLt7czuB zO#S46kU3$f{;m_Dpv5|4c7RkfE9!}|d|`u-S?hEgVl7+2IJW00Jm>?-sLxFk^;t=VMhGSgh zTeX8~o3K!DaaoVwkV$$Vb&;+^?@fqE1P#DkkW*?$C1w5lfz8}l8xuD7((>UmoKJ2* zeJFc?GwMYl03YoGMKYL()q39)!h(WbPXt>p2TpWqf_o#6?JqMoXO@ebGb;d<<|ez| zzlQx};xym30QxjhPR46DV*$7ZL0aNfs>xQn8#5z*@=>LxZHm4_JR59%B`FkLGO6X2PPWX>tH;1N-l+Kx>D`Xc3-&$@b~z!gFYFF5O-N=H z`MPX<48bE;YP~Q7S0S7hfvQMcSJ)FUI3dkcIO@Xn9Kw@y(mG)a@Z)ORmdJquacx6) zfN@4LJ>)K(9txFGLkWNkY5GtVpk0a$bxCvP-NUACN1I@dZTG7r^#%X_=|=laPtu%4 zdFmJccItoY$w;-+C6RV$4bB!em<^$+0^O&iCwdaM7uK7*XB36H_2e?NoLQ4TJ34D! zv#zC_j!Pbw{g5sgY?f}=Bmsjnv8R(OTj#h9b#Am0)B-)=gg}_G7`wE@4?8 z<0se9w+IEc8rdl<7ceq;K0QWAu(e2{u%gdgCzsMw1c9wb#KKB}c|smZr)l3QLFABw zt>!hAelNm?`+n#SxR|#?2v8OC5(WTp32(a)peo_T4*=lOnvp8mBz=iEfCb`7u&RbJ z$r|VmLW+Hi6L49L*K%+5tbOb_6)#r`0fkgIBA$cr}wK ziJ0&H{@bmy^|fAB%05Z$Sm5k%JMBnY5p5u@kTUH~ER?yVUZezF@R}NKoE?_QxoMqk z_VhS$1PjA=t13K4+U0%p6CxFhz)4l5Co>~2q@8Aj0>{fgazZt z!ZI;=uGF;DPQeP~!^APt@bX0}%W_Kq6Ny2jq3fWL z%?$nS)3Vpv5of;J{8?^Cyd9CxAr0C4jBNbWR+vZ+z$BUe`aE+Z`1G3?n2`Hnl5Oq^ zmQu|RbY#GOz5U%8AWjc|Wj^v6wXdD7R$Peqyr1*vajQO${@%o92BUxw#w6#tK>C3` zZ?-DGW`)Fa5pjf#pZx#hOJ~cxWV$Dx19~=mlxqp}uKmsyg8pH9_E literal 3442 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 diff --git a/src/export_png.zig b/src/export_png.zig index 43fd0b0..496e0dd 100644 --- a/src/export_png.zig +++ b/src/export_png.zig @@ -39,12 +39,17 @@ pub fn writeToPNGFile( sfc: surface.Surface, filename: []const u8, ) WriteToPNGFileError!void { - switch (sfc.getFormat()) { - .rgba, .rgb => {}, - else => { - return error.UnsupportedSurfaceFormat; - }, - } + // TODO: There are currently no unsupported surface formats, and there + // might likely not be going forward (we had alpha as an unsupported format + // but now we just convert it to greyscale). Keeping this here for a bit + // just in case, but we likely can remove it. + // + // switch (sfc.getFormat()) { + // .rgba, .rgb, .alpha8 => {}, + // else => { + // return error.UnsupportedSurfaceFormat; + // }, + // } // Open and create the file. const file = try fs.cwd().createFile(filename, .{}); @@ -73,12 +78,12 @@ fn writePNGIHDR(file: fs.File, sfc: surface.Surface) (Error || fs.File.WriteErro const depth: u8 = switch (sfc.getFormat()) { .rgba => 8, .rgb => 8, - else => return error.UnsupportedSurfaceFormat, + .alpha8 => 8, }; const color_type: u8 = switch (sfc.getFormat()) { .rgba => 6, .rgb => 2, - else => return error.UnsupportedSurfaceFormat, + .alpha8 => 0, }; const compression: u8 = 0; const filter: u8 = 0; @@ -177,7 +182,14 @@ fn writePNGIDATStream( ); break :written 4; // 4 bytes }, - else => return error.UnsupportedSurfaceFormat, + .alpha8 => |px| { + mem.copyForwards( + u8, + pixel_buffer[nbytes..pixel_buffer.len], + &@as([1]u8, @bitCast(px)), + ); + break :written 1; // 1 byte + }, } }; if (try zlib_stream.write(pixel_buffer[0..nbytes]) != nbytes) {