diff --git a/experiments/benchmark_cairo_draw.nim b/experiments/benchmark_cairo_draw.nim index c2f6cdf..778589d 100644 --- a/experiments/benchmark_cairo_draw.nim +++ b/experiments/benchmark_cairo_draw.nim @@ -1,4 +1,4 @@ -import benchy, cairo, pixie, pixie/blends +import benchy, cairo, pixie, pixie/blends, pixie/internal when defined(amd64) and not defined(pixieNoSimd): import nimsimd/sse2 @@ -7,10 +7,8 @@ when defined(release): {.push checks: off.} proc drawBasic(backdrop, source: Image) = - let sourceIsOpaque = source.isOpaque() - for y in 0 ..< min(backdrop.height, source.height): - if sourceIsOpaque: + if isOpaque(source.data, source.dataIndex(0, y), source.width): copyMem( backdrop.data[backdrop.dataIndex(0, y)].addr, source.data[source.dataIndex(0, y)].addr, diff --git a/src/pixie/images.nim b/src/pixie/images.nim index f64a6f3..b6b1072 100644 --- a/src/pixie/images.nim +++ b/src/pixie/images.nim @@ -182,29 +182,7 @@ proc isTransparent*(image: Image): bool {.raises: [].} = proc isOpaque*(image: Image): bool {.raises: [].} = ## Checks if the entire image is opaque (alpha values are all 255). - result = true - - var i: int - when defined(amd64) and not defined(pixieNoSimd): - let - vec255 = mm_set1_epi32(cast[int32](uint32.high)) - colorMask = mm_set1_epi32(cast[int32]([255.uint8, 255, 255, 0])) - for _ in 0 ..< image.data.len div 16: - let - values0 = mm_loadu_si128(image.data[i + 0].addr) - values1 = mm_loadu_si128(image.data[i + 4].addr) - values2 = mm_loadu_si128(image.data[i + 8].addr) - values3 = mm_loadu_si128(image.data[i + 12].addr) - values01 = mm_and_si128(values0, values1) - values23 = mm_and_si128(values2, values3) - values = mm_or_si128(mm_and_si128(values01, values23), colorMask) - if mm_movemask_epi8(mm_cmpeq_epi8(values, vec255)) != 0xffff: - return false - i += 16 - - for j in i ..< image.data.len: - if image.data[j].a != 255: - return false + isOpaque(image.data, 0, image.data.len) proc flipHorizontal*(image: Image) {.raises: [].} = ## Flips the image around the Y axis. @@ -789,9 +767,6 @@ proc drawUber( if yMin > 0 and yMin < a.height: zeroMem(a.data[0].addr, 4 * yMin * a.width) - when type(a) is Image and type(b) is Image: - let opaqueFastPath = blendMode in {bmNormal, bmOverwrite} and b.isOpaque() - for y in yMin ..< yMax: # Determine where we should start and stop drawing in the x dimension var @@ -852,7 +827,8 @@ proc drawUber( var x = xStart if not hasRotation: when type(a) is Image and type(b) is Image: - if opaqueFastPath: + if blendMode in {bmNormal, bmOverwrite} and + isOpaque(b.data, b.dataIndex(xStart, y), xStop - xStart): let srcPos = p + dx * x.float32 + dy * y.float32 sx = srcPos.x.int diff --git a/src/pixie/internal.nim b/src/pixie/internal.nim index 4bf2582..bff9dfb 100644 --- a/src/pixie/internal.nim +++ b/src/pixie/internal.nim @@ -96,6 +96,31 @@ proc toPremultipliedAlpha*(data: var seq[ColorRGBA | ColorRGBX]) {.raises: [].} c.b = ((c.b.uint32 * c.a.uint32) div 255).uint8 data[j] = c +proc isOpaque*(data: var seq[ColorRGBX], start, len: int): bool = + result = true + + var i: int + when defined(amd64) and not defined(pixieNoSimd): + let + vec255 = mm_set1_epi32(cast[int32](uint32.high)) + colorMask = mm_set1_epi32(cast[int32]([255.uint8, 255, 255, 0])) + for _ in 0 ..< len div 16: + let + values0 = mm_loadu_si128(data[i + 0].addr) + values1 = mm_loadu_si128(data[i + 4].addr) + values2 = mm_loadu_si128(data[i + 8].addr) + values3 = mm_loadu_si128(data[i + 12].addr) + values01 = mm_and_si128(values0, values1) + values23 = mm_and_si128(values2, values3) + values = mm_or_si128(mm_and_si128(values01, values23), colorMask) + if mm_movemask_epi8(mm_cmpeq_epi8(values, vec255)) != 0xffff: + return false + i += 16 + + for j in i ..< len: + if data[j].a != 255: + return false + when defined(amd64) and not defined(pixieNoSimd): proc packAlphaValues*(v: M128i): M128i {.inline, raises: [].} = ## Shuffle the alpha values for these 4 colors to the first 4 bytes