1
1
Fork 0
mirror of https://github.com/QB64-Phoenix-Edition/QB64pe.git synced 2024-07-06 17:30:22 +00:00

Merge pull request #241 from mkilgore/paint-fill-fix

Fix PAINT when border color is not supplied (QB64Official/qb64#38)
This commit is contained in:
Matt Kilgore 2022-11-09 20:39:28 -05:00 committed by GitHub
commit 4799c9b7d0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
21 changed files with 321 additions and 100 deletions

View file

@ -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) {

View file

@ -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

View file

@ -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'

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

View file

@ -0,0 +1 @@
Success, images are identical!

View file

@ -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'

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

View file

@ -0,0 +1 @@
Success, images are identical!

View file

@ -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'

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

View file

@ -0,0 +1 @@
Success, images are identical!

View file

@ -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'

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

View file

@ -0,0 +1 @@
Success, images are identical!

View file

@ -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'

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

View file

@ -0,0 +1 @@
Success, images are identical!

View file

@ -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'

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

View file

@ -0,0 +1 @@
Success, images are identical!

View file

@ -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