mirror of
https://github.com/DramaticShape/DramaticShapeVoxelMod.git
synced 2026-08-12 08:01:09 +02:00
account for dpi issues on updated 3d battles
This commit is contained in:
@@ -32,6 +32,28 @@
|
|||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
|
- **A staged battle on a phone stood some Pokémon three times the size of the
|
||||||
|
square they were on.** A Pidgey towered over the arena while the mon beside
|
||||||
|
it was the right size, which reads as a bug in one species and is not one.
|
||||||
|
|
||||||
|
Putting the paper back inside a battle pic (BattlePics, below) needs the
|
||||||
|
pic's pixels, and a LOVE Image does not hand them back -- so the pic is drawn
|
||||||
|
into a canvas of its own size and the canvas is read. `newCanvas` takes the
|
||||||
|
SURFACE's dpi scale when it is not told otherwise, `conf.lua` turns highdpi
|
||||||
|
on for Android and iOS, and Android's display density is routinely 2.75. So
|
||||||
|
`newCanvas(56, 56)` allocated a 154x154 texture there, the pic was magnified
|
||||||
|
into it, and the readback came back at the magnified size. The rebuilt pic
|
||||||
|
was 2.75x the artwork, the engine's pics layer drew it 1:1 because it trusts
|
||||||
|
`getWidth()`, and the mon stood on its tile nearly three times too big.
|
||||||
|
|
||||||
|
Only a pic with an enclosed hole in it is rebuilt at all -- the rest are
|
||||||
|
handed straight back untouched -- which is why it hit some species and not
|
||||||
|
others, and why it never showed on desktop, where the dpi scale is already 1.
|
||||||
|
The readback now asks for one texel per pic pixel, the way the engine's own
|
||||||
|
`PixelCanvas` does for the same reason. The animated-tile atlas readback took
|
||||||
|
the same fix: on a phone it would have come back magnified too, and every
|
||||||
|
tile coordinate in it counts in eights from the top-left.
|
||||||
|
|
||||||
- **Battle pics were see-through, and it took a back sprite on a tiled floor
|
- **Battle pics were see-through, and it took a back sprite on a tiled floor
|
||||||
to make it obvious.** Gen 1 pics are two-bit art whose lightest shade is
|
to make it obvious.** Gen 1 pics are two-bit art whose lightest shade is
|
||||||
white, and the decoded PNGs key that shade to alpha 0 -- which cost nothing
|
white, and the decoded PNGs key that shade to alpha 0 -- which cost nothing
|
||||||
|
|||||||
+16
-1
@@ -88,6 +88,21 @@ local CUT = 0.5
|
|||||||
-- its data back, so it is drawn into a canvas of its own size and the canvas
|
-- its data back, so it is drawn into a canvas of its own size and the canvas
|
||||||
-- is read -- which is also what makes this work for every path that produces
|
-- is read -- which is also what makes this work for every path that produces
|
||||||
-- a pic, without knowing which one produced this one.
|
-- a pic, without knowing which one produced this one.
|
||||||
|
--
|
||||||
|
-- The canvas is forced to dpiscale = 1, and that is the whole difference
|
||||||
|
-- between a pic and a MONSTER. love.graphics.newCanvas defaults its dpiscale
|
||||||
|
-- to the surface's, conf.lua turns highdpi on for Android and iOS, and
|
||||||
|
-- Android's density is routinely 2.75 -- so newCanvas(56, 56) hands back a
|
||||||
|
-- 154x154 texture there, the pic is drawn into it magnified to fill it, and
|
||||||
|
-- newImageData reads the magnified copy back at its own PIXEL size. The image
|
||||||
|
-- built from that is 2.75x the artwork, drawPicsLayer draws it at 1:1 because
|
||||||
|
-- it trusts getWidth(), and the mon stands on the map nearly three times the
|
||||||
|
-- size of the square it is supposed to cover. Desktop never saw it: dpiscale
|
||||||
|
-- is already 1 there. Nor did every species, because only a pic with an
|
||||||
|
-- enclosed hole in it comes back through here at all (see `changed` below) --
|
||||||
|
-- so a Pidgey came out giant and the mon beside it did not, which is what
|
||||||
|
-- makes this read as a sprite bug rather than a scale one. See the engine's
|
||||||
|
-- own src/render/PixelCanvas.lua, which exists for exactly this reason.
|
||||||
local function readBack(img)
|
local function readBack(img)
|
||||||
local w, h = img:getDimensions()
|
local w, h = img:getDimensions()
|
||||||
if w <= 0 or h <= 0 then return nil end
|
if w <= 0 or h <= 0 then return nil end
|
||||||
@@ -96,7 +111,7 @@ local function readBack(img)
|
|||||||
local prevR, prevG, prevB, prevA = love.graphics.getColor()
|
local prevR, prevG, prevB, prevA = love.graphics.getColor()
|
||||||
local data = nil
|
local data = nil
|
||||||
local ok = pcall(function()
|
local ok = pcall(function()
|
||||||
local canvas = love.graphics.newCanvas(w, h)
|
local canvas = love.graphics.newCanvas(w, h, { dpiscale = 1 })
|
||||||
love.graphics.setCanvas(canvas)
|
love.graphics.setCanvas(canvas)
|
||||||
love.graphics.clear(0, 0, 0, 0)
|
love.graphics.clear(0, 0, 0, 0)
|
||||||
love.graphics.setBlendMode("replace", "premultiplied")
|
love.graphics.setBlendMode("replace", "premultiplied")
|
||||||
|
|||||||
@@ -270,7 +270,12 @@ local function readback(image)
|
|||||||
local prev = love.graphics.getCanvas()
|
local prev = love.graphics.getCanvas()
|
||||||
local ok, data = pcall(function()
|
local ok, data = pcall(function()
|
||||||
local w, h = image:getDimensions()
|
local w, h = image:getDimensions()
|
||||||
local canvas = love.graphics.newCanvas(w, h)
|
-- dpiscale = 1, or this is not a copy. On a highdpi surface (Android,
|
||||||
|
-- iOS -- see conf.lua) newCanvas takes the surface's scale by default,
|
||||||
|
-- so the atlas would be drawn into a texture 2.75x its size and read
|
||||||
|
-- back magnified -- and every tile coordinate below, which counts in
|
||||||
|
-- eights from the top-left, would land somewhere between two tiles.
|
||||||
|
local canvas = love.graphics.newCanvas(w, h, { dpiscale = 1 })
|
||||||
love.graphics.setCanvas(canvas)
|
love.graphics.setCanvas(canvas)
|
||||||
love.graphics.clear(0, 0, 0, 0)
|
love.graphics.clear(0, 0, 0, 0)
|
||||||
-- straight copy: no blending against the cleared target, no tint from
|
-- straight copy: no blending against the cleared target, no tint from
|
||||||
|
|||||||
@@ -2170,6 +2170,7 @@ local BattlePics = run.loader.exports.DRAMATIC_SHAPE.lib.require("BattlePics")
|
|||||||
-- Run one hand-drawn figure through the real BattlePics and hand back a
|
-- Run one hand-drawn figure through the real BattlePics and hand back a
|
||||||
-- reader over what came out. The pic is faked at the readback seam, which is
|
-- reader over what came out. The pic is faked at the readback seam, which is
|
||||||
-- the only thing between this and the pixels the engine would have blitted.
|
-- the only thing between this and the pixels the engine would have blitted.
|
||||||
|
local lastCanvas = nil -- what the readback asked newCanvas for
|
||||||
local function fill(rows)
|
local function fill(rows)
|
||||||
local W, H = #rows[1], #rows
|
local W, H = #rows[1], #rows
|
||||||
local built = nil
|
local built = nil
|
||||||
@@ -2189,7 +2190,8 @@ local function fill(rows)
|
|||||||
end
|
end
|
||||||
|
|
||||||
local realNewCanvas, realNewImage = love.graphics.newCanvas, love.graphics.newImage
|
local realNewCanvas, realNewImage = love.graphics.newCanvas, love.graphics.newImage
|
||||||
love.graphics.newCanvas = function()
|
love.graphics.newCanvas = function(cw, ch, opts)
|
||||||
|
lastCanvas = { w = cw, h = ch, opts = opts }
|
||||||
return { setFilter = function() end, release = function() end,
|
return { setFilter = function() end, release = function() end,
|
||||||
newImageData = fakeData }
|
newImageData = fakeData }
|
||||||
end
|
end
|
||||||
@@ -2285,6 +2287,24 @@ local drainOut, drainPic, drain = fill({
|
|||||||
T.check(drainOut ~= drainPic and drain and drain(8, 4),
|
T.check(drainOut ~= drainPic and drain and drain(8, 4),
|
||||||
"a narrow one is where the drawing ran out, and is paper")
|
"a narrow one is where the drawing ran out, and is paper")
|
||||||
|
|
||||||
|
-- ------- and the readback is measured in PIXELS, which is what kept the mons
|
||||||
|
-- the size of the squares they stand on
|
||||||
|
--
|
||||||
|
-- love.graphics.newCanvas takes the SURFACE's dpi scale when it is not told
|
||||||
|
-- otherwise, conf.lua turns highdpi on for Android and iOS, and Android's
|
||||||
|
-- density is routinely 2.75. So an untold newCanvas(56, 56) allocated a
|
||||||
|
-- 154x154 texture on a phone, the pic was magnified into it, and newImageData
|
||||||
|
-- read the magnified copy back at its own size -- an image 2.75x the artwork,
|
||||||
|
-- which drawPicsLayer then drew at 1:1 because it trusts getWidth(). The mon
|
||||||
|
-- stood on the map three times the size of its tile.
|
||||||
|
--
|
||||||
|
-- Only for a pic with paper to put back, which is why it read as a bug in
|
||||||
|
-- particular Pokemon (a giant Pidgey beside a normal mon) rather than as a
|
||||||
|
-- scale that was wrong everywhere.
|
||||||
|
T.check(lastCanvas and lastCanvas.opts and lastCanvas.opts.dpiscale == 1,
|
||||||
|
"the readback canvas is one texel per pic pixel, on a highdpi phone too")
|
||||||
|
T.eq(lastCanvas.w, 18, "and it is the size of the pic, in those pixels")
|
||||||
|
|
||||||
-- the answer is cached on the image, so a pic costs one readback a session
|
-- the answer is cached on the image, so a pic costs one readback a session
|
||||||
-- rather than one a frame -- checked on the first figure, which is still in
|
-- rather than one a frame -- checked on the first figure, which is still in
|
||||||
-- there because fill() does not clear it
|
-- there because fill() does not clear it
|
||||||
|
|||||||
Reference in New Issue
Block a user