diff --git a/internal/c/libqb.cpp b/internal/c/libqb.cpp index d3d4e78e6..bf736ac01 100644 --- a/internal/c/libqb.cpp +++ b/internal/c/libqb.cpp @@ -13503,128 +13503,98 @@ void sub_paint(float x, float y, qbs *fillstr, uint32 bordercol, qbs *background done = (uint8 *)calloc(i, 1); } - // return if first point is the bordercolor - if (qbg_active_page_offset[iy * qbg_width + ix] == bordercol) + // The original color of the starting location + uint32_t startingColor = qbg_active_page_offset[iy * qbg_width + ix]; + + bool borderColorProvided = passed & 4; + + // Exit early if we're already at the border color + if (borderColorProvided && qbg_active_page_offset[iy * qbg_width + ix] == bordercol) return; // create first node a_x[0] = ix; a_y[0] = iy; a_t[0] = 15; - // types: - //&1=check left - //&2=check right - //&4=check above - //&8=check below a_n = 1; qbg_active_page_offset[iy * qbg_width + ix] = tile[ix % sx][iy % sy]; done[iy * qbg_width + ix] = 1; -nextpass: - b_n = 0; - for (i = 0; i < a_n; i++) { - t = a_t[i]; - ix = a_x[i]; - iy = a_y[i]; + // Each index maps to a direction, in the order: + // Left, Right, Up, Down + int32_t xdelta[4] = { -1, 1, 0, 0 }; + int32_t ydelta[4] = { 0, 0, -1, 1 }; - // left - if (t & 1) { - x2 = ix - 1; - y2 = iy; - if (x2 >= qbg_view_x1) { - offset = y2 * qbg_width + x2; - if (!done[offset]) { - done[offset] = 1; - if (qbg_active_page_offset[offset] != bordercol) { - qbg_active_page_offset[offset] = tile[x2 % sx][y2 % sy]; - b_t[b_n] = 13; - b_x[b_n] = x2; - b_y[b_n] = y2; - b_n++; // add new node + // The bits indicate the directions that should be checked for the next + // pixel, we ignore the direction we came from. + uint32_t dirCheckMap[4] = { + 1 | 4 | 8, // Left, Up, Down + 2 | 4 | 8, // Right, Up, Down + 1 | 2 | 4, // Left, Right, Up + 1 | 2 | 8, // Left, Right, Down + }; + + while (true) { + b_n = 0; + for (i = 0; i < a_n; i++) { + t = a_t[i]; + ix = a_x[i]; + iy = a_y[i]; + + for (int k = 0; k < 4; k++) { + if ((t & (1 << k)) == 0) + continue; + + x2 = ix + xdelta[k]; + y2 = iy + ydelta[k]; + + // Verify dimensions are within bounds + if (x2 >= qbg_view_x1 && x2 <= qbg_view_x2 && y2 >= qbg_view_y1 && y2 <= qbg_view_y2) { + offset = y2 * qbg_width + x2; + + // Check that we haven't done this pixel yet + if (!done[offset]) { + done[offset] = 1; + + // We either check that we didn't hit the border color + // (if provided), or that we're still the starting + // color. + if ((borderColorProvided && qbg_active_page_offset[offset] != bordercol) + || (!borderColorProvided && qbg_active_page_offset[offset] == startingColor)) { + + qbg_active_page_offset[offset] = tile[x2 % sx][y2 % sy]; + b_t[b_n] = dirCheckMap[k]; + b_x[b_n] = x2; + b_y[b_n] = y2; + b_n++; // add new node + } } } } } - // right - if (t & 2) { - x2 = ix + 1; - y2 = iy; - if (x2 <= qbg_view_x2) { - offset = y2 * qbg_width + x2; - if (!done[offset]) { - done[offset] = 1; - if (qbg_active_page_offset[offset] != bordercol) { - qbg_active_page_offset[offset] = tile[x2 % sx][y2 % sy]; - b_t[b_n] = 14; - b_x[b_n] = x2; - b_y[b_n] = y2; - b_n++; // add new node - } - } - } + // no new nodes? + if (b_n == 0) { + memset(done, 0, write_page->width * write_page->height); // cleanup + return; // finished! } - // above - if (t & 4) { - x2 = ix; - y2 = iy - 1; - if (y2 >= qbg_view_y1) { - offset = y2 * qbg_width + x2; - if (!done[offset]) { - done[offset] = 1; - if (qbg_active_page_offset[offset] != bordercol) { - qbg_active_page_offset[offset] = tile[x2 % sx][y2 % sy]; - b_t[b_n] = 7; - b_x[b_n] = x2; - b_y[b_n] = y2; - b_n++; // add new node - } - } - } - } + // swap a & b arrays + sp = a_x; + a_x = b_x; + b_x = sp; - // below - if (t & 8) { - x2 = ix; - y2 = iy + 1; - if (y2 <= qbg_view_y2) { - offset = y2 * qbg_width + x2; - if (!done[offset]) { - done[offset] = 1; - if (qbg_active_page_offset[offset] != bordercol) { - qbg_active_page_offset[offset] = tile[x2 % sx][y2 % sy]; - b_t[b_n] = 11; - b_x[b_n] = x2; - b_y[b_n] = y2; - b_n++; // add new node - } - } - } - } + sp = a_y; + a_y = b_y; + b_y = sp; - } // i + cp = a_t; + a_t = b_t; + b_t = cp; - // no new nodes? - if (b_n == 0) { - memset(done, 0, write_page->width * write_page->height); // cleanup - return; // finished! + a_n = b_n; } - - // swap a & b arrays - sp = a_x; - a_x = b_x; - b_x = sp; - sp = a_y; - a_y = b_y; - b_y = sp; - cp = a_t; - a_t = b_t; - b_t = cp; - a_n = b_n; - - goto nextpass; } void sub_circle(double x, double y, double r, uint32 col, double start, double end, double aspect, int32 passed) { diff --git a/tests/compile_tests.sh b/tests/compile_tests.sh index bddf194d5..de0e72aaa 100755 --- a/tests/compile_tests.sh +++ b/tests/compile_tests.sh @@ -107,7 +107,7 @@ do pushd . > /dev/null cd "./tests/compile_tests/$category" - testResult=$("../../../$EXE" 2>&1) + testResult=$("../../../$EXE" "../../../$RESULTS_DIR" "$category-$testName" 2>&1) ERR=$? popd > /dev/null diff --git a/tests/compile_tests/paint/tile_border_black_bg.bas b/tests/compile_tests/paint/tile_border_black_bg.bas new file mode 100644 index 000000000..83784e305 --- /dev/null +++ b/tests/compile_tests/paint/tile_border_black_bg.bas @@ -0,0 +1,19 @@ +$Console:Only +ChDir _StartDir$ + +TILE$ = MKL$(&HAAFFBB55) + +' Paint with tiling, white border color. +' background is black, circle is white. +' +' Result should fill the entire circle with pattern +test1& = _NewImage(128, 50, 9) +_Dest test1& + +Circle (64, 25), 25, 7 +Paint (64, 25), TILE$, 7 + +AssertImage test1&, "tile_border_black_bg.bmp" +System + +'$include:'../utilities/imageassert.bm' diff --git a/tests/compile_tests/paint/tile_border_black_bg.bmp b/tests/compile_tests/paint/tile_border_black_bg.bmp new file mode 100644 index 000000000..d5a59bc38 Binary files /dev/null and b/tests/compile_tests/paint/tile_border_black_bg.bmp differ diff --git a/tests/compile_tests/paint/tile_border_black_bg.output b/tests/compile_tests/paint/tile_border_black_bg.output new file mode 100644 index 000000000..b86db1d47 --- /dev/null +++ b/tests/compile_tests/paint/tile_border_black_bg.output @@ -0,0 +1 @@ +Success, images are identical! diff --git a/tests/compile_tests/paint/tile_border_black_nocolor.bas b/tests/compile_tests/paint/tile_border_black_nocolor.bas new file mode 100644 index 000000000..d6bf51b40 --- /dev/null +++ b/tests/compile_tests/paint/tile_border_black_nocolor.bas @@ -0,0 +1,23 @@ +$Console:Only +ChDir _StartDir$ + +TILE$ = MKL$(&HAAFFBB55) + +' Paint with tiling, black border color. +' background is colored white, circle is black. +' +' Result should no change anything, as the inside +' of the circle matches the border color. +test1& = _NewImage(128, 50, 9) +_Dest test1& + +' Make the entire image white +Line (0, 0)-(127, 49), 7, BF + +Circle (64, 25), 25, 0 +Paint (64, 25), TILE$, 7 + +AssertImage test1&, "tile_border_black_nocolor.bmp" +System + +'$include:'../utilities/imageassert.bm' diff --git a/tests/compile_tests/paint/tile_border_black_nocolor.bmp b/tests/compile_tests/paint/tile_border_black_nocolor.bmp new file mode 100644 index 000000000..bdf2e6b60 Binary files /dev/null and b/tests/compile_tests/paint/tile_border_black_nocolor.bmp differ diff --git a/tests/compile_tests/paint/tile_border_black_nocolor.output b/tests/compile_tests/paint/tile_border_black_nocolor.output new file mode 100644 index 000000000..b86db1d47 --- /dev/null +++ b/tests/compile_tests/paint/tile_border_black_nocolor.output @@ -0,0 +1 @@ +Success, images are identical! diff --git a/tests/compile_tests/paint/tile_border_white_bg.bas b/tests/compile_tests/paint/tile_border_white_bg.bas new file mode 100644 index 000000000..d897c89ab --- /dev/null +++ b/tests/compile_tests/paint/tile_border_white_bg.bas @@ -0,0 +1,28 @@ +$Console:Only +ChDir _StartDir$ + +TILE$ = MKL$(&HAAFFBB55) + +' Paint with tiling, black border color. +' background is colored white, circle is black. +' +' Note the starting location for Paint is neither +' white or black, the starting color should not +' matter when using a border color. +' +' Result should fill the entire circle with pattern +test1& = _NewImage(128, 50, 9) +_Dest test1& + +' Make the entire image white +Line (0, 0)-(127, 49), 7, BF + +Circle (64, 25), 25, 0 +Pset (64, 25), 15 ' Set the starting location a third color + +Paint (64, 25), TILE$, 0 + +AssertImage test1&, "tile_border_white_bg.bmp" +System + +'$include:'../utilities/imageassert.bm' diff --git a/tests/compile_tests/paint/tile_border_white_bg.bmp b/tests/compile_tests/paint/tile_border_white_bg.bmp new file mode 100644 index 000000000..35a79c041 Binary files /dev/null and b/tests/compile_tests/paint/tile_border_white_bg.bmp differ diff --git a/tests/compile_tests/paint/tile_border_white_bg.output b/tests/compile_tests/paint/tile_border_white_bg.output new file mode 100644 index 000000000..b86db1d47 --- /dev/null +++ b/tests/compile_tests/paint/tile_border_white_bg.output @@ -0,0 +1 @@ +Success, images are identical! diff --git a/tests/compile_tests/paint/tile_border_white_nocolor.bas b/tests/compile_tests/paint/tile_border_white_nocolor.bas new file mode 100644 index 000000000..ba3f52c57 --- /dev/null +++ b/tests/compile_tests/paint/tile_border_white_nocolor.bas @@ -0,0 +1,23 @@ +$Console:Only +ChDir _StartDir$ + +TILE$ = MKL$(&HAAFFBB55) + +' Paint with tiling, white border color. +' background is colored white, circle is black. +' +' Result should no change anything, as the inside +' of the circle matches the border color. +test1& = _NewImage(128, 50, 9) +_Dest test1& + +' Make the entire image white +Line (0, 0)-(127, 49), 7, BF + +Circle (64, 25), 25, 0 +Paint (64, 25), TILE$, 7 + +AssertImage test1&, "tile_border_white_nocolor.bmp" +System + +'$include:'../utilities/imageassert.bm' diff --git a/tests/compile_tests/paint/tile_border_white_nocolor.bmp b/tests/compile_tests/paint/tile_border_white_nocolor.bmp new file mode 100644 index 000000000..bdf2e6b60 Binary files /dev/null and b/tests/compile_tests/paint/tile_border_white_nocolor.bmp differ diff --git a/tests/compile_tests/paint/tile_border_white_nocolor.output b/tests/compile_tests/paint/tile_border_white_nocolor.output new file mode 100644 index 000000000..b86db1d47 --- /dev/null +++ b/tests/compile_tests/paint/tile_border_white_nocolor.output @@ -0,0 +1 @@ +Success, images are identical! diff --git a/tests/compile_tests/paint/tile_noborder_black_bg.bas b/tests/compile_tests/paint/tile_noborder_black_bg.bas new file mode 100644 index 000000000..f7fb58048 --- /dev/null +++ b/tests/compile_tests/paint/tile_noborder_black_bg.bas @@ -0,0 +1,19 @@ +$Console:Only +ChDir _StartDir$ + +TILE$ = MKL$(&HAAFFBB55) + +' Paint with tiling, no border color. +' background is left black. +' +' Result should fill the entire circle with pattern +test1& = _NewImage(128, 50, 9) +_Dest test1& + +Circle (64, 25), 25, 7 +Paint (64, 25), TILE$ + +AssertImage test1&, "tile_noborder_black_bg.bmp" +System + +'$include:'../utilities/imageassert.bm' diff --git a/tests/compile_tests/paint/tile_noborder_black_bg.bmp b/tests/compile_tests/paint/tile_noborder_black_bg.bmp new file mode 100644 index 000000000..d5a59bc38 Binary files /dev/null and b/tests/compile_tests/paint/tile_noborder_black_bg.bmp differ diff --git a/tests/compile_tests/paint/tile_noborder_black_bg.output b/tests/compile_tests/paint/tile_noborder_black_bg.output new file mode 100644 index 000000000..b86db1d47 --- /dev/null +++ b/tests/compile_tests/paint/tile_noborder_black_bg.output @@ -0,0 +1 @@ +Success, images are identical! diff --git a/tests/compile_tests/paint/tile_noborder_white_bg.bas b/tests/compile_tests/paint/tile_noborder_white_bg.bas new file mode 100644 index 000000000..9987a4bd4 --- /dev/null +++ b/tests/compile_tests/paint/tile_noborder_white_bg.bas @@ -0,0 +1,22 @@ +$Console:Only +ChDir _StartDir$ + +TILE$ = MKL$(&HAAFFBB55) + +' Paint with tiling, no border color. +' background is colored white, circle is black. +' +' Result should fill the entire circle with pattern +test1& = _NewImage(128, 50, 9) +_Dest test1& + +' Make the entire image white +Line (0, 0)-(127, 49), 7, BF + +Circle (64, 25), 25, 0 +Paint (64, 25), TILE$ + +AssertImage test1&, "tile_noborder_white_bg.bmp" +System + +'$include:'../utilities/imageassert.bm' diff --git a/tests/compile_tests/paint/tile_noborder_white_bg.bmp b/tests/compile_tests/paint/tile_noborder_white_bg.bmp new file mode 100644 index 000000000..35a79c041 Binary files /dev/null and b/tests/compile_tests/paint/tile_noborder_white_bg.bmp differ diff --git a/tests/compile_tests/paint/tile_noborder_white_bg.output b/tests/compile_tests/paint/tile_noborder_white_bg.output new file mode 100644 index 000000000..b86db1d47 --- /dev/null +++ b/tests/compile_tests/paint/tile_noborder_white_bg.output @@ -0,0 +1 @@ +Success, images are identical! diff --git a/tests/compile_tests/utilities/imageassert.bm b/tests/compile_tests/utilities/imageassert.bm new file mode 100644 index 000000000..c3e3b0786 --- /dev/null +++ b/tests/compile_tests/utilities/imageassert.bm @@ -0,0 +1,111 @@ + +' +' Asserts that a created image, 'originalActualImage', is identical to the provide expected image, 'expectedFileName' +' +' The created image is converted to 32-bit and saved to the results folder. +' We then load the expected image as a 32-bit image, compare file sizes, +' width/height, and each pixel. +' +SUB AssertImage(originalActualImage As Long, expectedFileName As String) + Dim actualImage As Long + Dim ResultsDir As String, TestPrefix As String + + ResultsDir = Command$(1) + TestPrefix = Command$(2) + + ' Make sure the test result will be seen + _Dest _Console + + ' Convert to 32-bit for comparisons + actualImage = _NewImage(_Width(originalActualImage), _Height(originalActualImage), 32) + _PUTIMAGE , originalActualImage, actualImage + + 'First save the result + SaveImage actualImage, ResultsDir + "/" + TestPrefix + "_result.bmp" + + 'Compare both images, print whether they are identical + Dim expectedImage As Long + expectedImage = _LOADIMAGE(expectedFileName, 32) + + If _Width(actualImage) <> _Width(expectedImage) Then + Print "Failure! Image width differs, actual:"; _Width(actualImage);", Expected:"; _Width(expectedImage) + GoTo freeImages + End If + + If _Height(actualImage) <> _Height(expectedImage) Then + Print "Failure! Image height differs, actual:"; _Height(actualImage);", Expected:"; _Height(expectedImage) + GoTo freeImages + End If + + Dim actual As _Mem, expected As _Mem + + actual = _MEMIMAGE(actualImage) + expected = _MEMIMAGE(expectedImage) + + IF actual.SIZE <> expected.SIZE THEN + Print "Failure! Image sizes differ, Actual:"; actual.SIZE; ", Expected:"; expected.SIZE + GoTo freeImages + END IF + + w& = _Width(expectedImage) + h& = _Height(expectedImage) + + For x& = 0 to w& - 1 + For y& = 0 to h& - 1 + pixelOffset = (y& * w& + x&) * 4 + + actualPixel& = _MemGet(actual, actual.OFFSET + pixelOffset, LONG) + expectedPixel& = _MemGet(expected, expected.OFFSET + pixelOffset, LONG) + + If actualPixel& <> expectedPixel& Then + Print "Failure! Image pixels at ("; x&; ","; y&; ") differ, actual: 0x"; HEX$(actualPixel&);", expected: 0x"; HEX$(expectedPixel&) + GoTo freeImages + End If + Next + Next + + PRINT "Success, images are identical!" + +freeImages: + _MEMFREE actual + _MEMFREE expected + _FreeImage actualImage +END SUB + +' From the QB64-PE Wiki: https://qb64phoenix.com/qb64wiki/index.php/SAVEIMAGE +Sub SaveImage (image As Long, filename As String) + bytesperpixel& = _PixelSize(image&) + If bytesperpixel& = 0 Then Print "Text modes unsupported!": End + If bytesperpixel& = 1 Then bpp& = 8 Else bpp& = 24 + x& = _Width(image&) + y& = _Height(image&) + b$ = "BM????QB64????" + MKL$(40) + MKL$(x&) + MKL$(y&) + MKI$(1) + MKI$(bpp&) + MKL$(0) + "????" + String$(16, 0) 'partial BMP header info(???? to be filled later) + If bytesperpixel& = 1 Then + For c& = 0 To 255 ' read BGR color settings from JPG image + 1 byte spacer(CHR$(0)) + cv& = _PaletteColor(c&, image&) ' color attribute to read. + b$ = b$ + Chr$(_Blue32(cv&)) + Chr$(_Green32(cv&)) + Chr$(_Red32(cv&)) + Chr$(0) 'spacer byte + Next + End If + Mid$(b$, 11, 4) = MKL$(Len(b$)) ' image pixel data offset(BMP header) + lastsource& = _Source + _Source image& + If ((x& * 3) Mod 4) Then padder$ = String$(4 - ((x& * 3) Mod 4), 0) + For py& = y& - 1 To 0 Step -1 ' read JPG image pixel color data + r$ = "" + For px& = 0 To x& - 1 + c& = Point(px&, py&) 'POINT 32 bit values are large LONG values + If bytesperpixel& = 1 Then r$ = r$ + Chr$(c&) Else r$ = r$ + Left$(MKL$(c&), 3) + Next px& + d$ = d$ + r$ + padder$ + Next py& + _Source lastsource& + Mid$(b$, 35, 4) = MKL$(Len(d$)) ' image size(BMP header) + b$ = b$ + d$ ' total file data bytes to create file + Mid$(b$, 3, 4) = MKL$(Len(b$)) ' size of data file(BMP header) + If LCase$(Right$(filename$, 4)) <> ".bmp" Then ext$ = ".bmp" + f& = FreeFile + Open filename$ + ext$ For Output As #f&: Close #f& ' erases an existing file + Open filename$ + ext$ For Binary As #f& + Put #f&, , b$ + Close #f& +End Sub