add conflict resolution mode for SWF screenshots

The "Dyeworks Pink: Peaceful Tree Garland" was a tricky case, with animated falling leaves…

we decided that having transparency in the main pet area, and some incorrect transparent holes in the trees, was a better conflict resolution for this one

Probably would be good to manually upload a Totally Good version, but like, this flag is probably good to have
This commit is contained in:
Emi Matchu 2020-08-12 01:32:25 -07:00
parent 71e1112b63
commit 0f97693500

View file

@ -10,6 +10,7 @@ import {
ModalFooter, ModalFooter,
ModalHeader, ModalHeader,
ModalOverlay, ModalOverlay,
Select,
useToast, useToast,
} from "@chakra-ui/core"; } from "@chakra-ui/core";
import { ExternalLinkIcon } from "@chakra-ui/icons"; import { ExternalLinkIcon } from "@chakra-ui/icons";
@ -33,6 +34,8 @@ function ItemLayerSupportUploadModal({ item, itemLayer, isOpen, onClose }) {
const [isUploading, setIsUploading] = React.useState(false); const [isUploading, setIsUploading] = React.useState(false);
const [uploadError, setUploadError] = React.useState(null); const [uploadError, setUploadError] = React.useState(null);
const [conflictMode, setConflictMode] = React.useState("onBlack");
const supportSecret = useSupportSecret(); const supportSecret = useSupportSecret();
const toast = useToast(); const toast = useToast();
const apolloClient = useApolloClient(); const apolloClient = useApolloClient();
@ -47,14 +50,16 @@ function ItemLayerSupportUploadModal({ item, itemLayer, isOpen, onClose }) {
setNumWarnings(null); setNumWarnings(null);
setIsUploading(false); setIsUploading(false);
mergeIntoImageWithAlpha(imageOnBlackUrl, imageOnWhiteUrl).then( mergeIntoImageWithAlpha(
([url, blob, numWarnings]) => { imageOnBlackUrl,
imageOnWhiteUrl,
conflictMode
).then(([url, blob, numWarnings]) => {
setImageWithAlphaUrl(url); setImageWithAlphaUrl(url);
setImageWithAlphaBlob(blob); setImageWithAlphaBlob(blob);
setNumWarnings(numWarnings); setNumWarnings(numWarnings);
} });
); }, [imageOnBlackUrl, imageOnWhiteUrl, conflictMode]);
}, [imageOnBlackUrl, imageOnWhiteUrl]);
const onUpload = React.useCallback( const onUpload = React.useCallback(
(e) => { (e) => {
@ -167,6 +172,8 @@ function ItemLayerSupportUploadModal({ item, itemLayer, isOpen, onClose }) {
<ItemLayerSupportReviewStep <ItemLayerSupportReviewStep
imageWithAlphaUrl={imageWithAlphaUrl} imageWithAlphaUrl={imageWithAlphaUrl}
numWarnings={numWarnings} numWarnings={numWarnings}
conflictMode={conflictMode}
onChangeConflictMode={setConflictMode}
/> />
)} )}
</ModalBody> </ModalBody>
@ -250,7 +257,12 @@ function ItemLayerSupportScreenshotStep({ itemLayer, step, onUpload }) {
); );
} }
function ItemLayerSupportReviewStep({ imageWithAlphaUrl, numWarnings }) { function ItemLayerSupportReviewStep({
imageWithAlphaUrl,
numWarnings,
conflictMode,
onChangeConflictMode,
}) {
if (imageWithAlphaUrl == null) { if (imageWithAlphaUrl == null) {
return <Box>Generating image</Box>; return <Box>Generating image</Box>;
} }
@ -283,6 +295,31 @@ function ItemLayerSupportReviewStep({ imageWithAlphaUrl, numWarnings }) {
/> />
)} )}
</Box> </Box>
<Box>
{numWarnings > 0 && (
<Box
display="flex"
flexDirection="row"
alignItems="center"
justifyContent="center"
width="600px"
marginTop="2"
>
<Box flex="0 1 auto" marginRight="2">
When pixels conflict, we use
</Box>
<Select
flex="0 0 200px"
value={conflictMode}
onChange={(e) => onChangeConflictMode(e.target.value)}
>
<option value="onBlack">the version on black</option>
<option value="onWhite">the version on white</option>
<option value="transparent">transparent pixels</option>
</Select>
</Box>
)}
</Box>
</> </>
); );
} }
@ -386,7 +423,11 @@ function ItemLayerSupportFlashPlayer({ swfUrl, backgroundColor }) {
); );
} }
async function mergeIntoImageWithAlpha(imageOnBlackUrl, imageOnWhiteUrl) { async function mergeIntoImageWithAlpha(
imageOnBlackUrl,
imageOnWhiteUrl,
conflictMode
) {
const [imageOnBlack, imageOnWhite] = await Promise.all([ const [imageOnBlack, imageOnWhite] = await Promise.all([
readImageDataFromUrl(imageOnBlackUrl), readImageDataFromUrl(imageOnBlackUrl),
readImageDataFromUrl(imageOnWhiteUrl), readImageDataFromUrl(imageOnWhiteUrl),
@ -394,7 +435,8 @@ async function mergeIntoImageWithAlpha(imageOnBlackUrl, imageOnWhiteUrl) {
const [imageWithAlphaData, numWarnings] = mergeDataIntoImageWithAlpha( const [imageWithAlphaData, numWarnings] = mergeDataIntoImageWithAlpha(
imageOnBlack, imageOnBlack,
imageOnWhite imageOnWhite,
conflictMode
); );
const [ const [
imageWithAlphaUrl, imageWithAlphaUrl,
@ -404,7 +446,7 @@ async function mergeIntoImageWithAlpha(imageOnBlackUrl, imageOnWhiteUrl) {
return [imageWithAlphaUrl, imageWithAlphaBlob, numWarnings]; return [imageWithAlphaUrl, imageWithAlphaBlob, numWarnings];
} }
function mergeDataIntoImageWithAlpha(imageOnBlack, imageOnWhite) { function mergeDataIntoImageWithAlpha(imageOnBlack, imageOnWhite, conflictMode) {
const imageWithAlpha = new ImageData(600, 600); const imageWithAlpha = new ImageData(600, 600);
let numWarnings = 0; let numWarnings = 0;
@ -428,13 +470,29 @@ function mergeDataIntoImageWithAlpha(imageOnBlack, imageOnWhite) {
` vs ` + ` vs ` +
`#${rOnBlack.toString(16)}${bOnBlack.toString(16)}` + `#${rOnBlack.toString(16)}${bOnBlack.toString(16)}` +
`${gOnWhite.toString(16)}. ` + `${gOnWhite.toString(16)}. ` +
`Falling back to the pixel on black, with alpha = 100%. ` `Using conflict mode ${conflictMode} to fall back.`
); );
} }
if (conflictMode === "onBlack") {
imageWithAlpha.data[pixelIndex] = rOnBlack; imageWithAlpha.data[pixelIndex] = rOnBlack;
imageWithAlpha.data[pixelIndex + 1] = gOnBlack; imageWithAlpha.data[pixelIndex + 1] = gOnBlack;
imageWithAlpha.data[pixelIndex + 2] = bOnBlack; imageWithAlpha.data[pixelIndex + 2] = bOnBlack;
imageWithAlpha.data[pixelIndex + 3] = 255; imageWithAlpha.data[pixelIndex + 3] = 255;
} else if (conflictMode === "onWhite") {
imageWithAlpha.data[pixelIndex] = rOnWhite;
imageWithAlpha.data[pixelIndex + 1] = gOnWhite;
imageWithAlpha.data[pixelIndex + 2] = bOnWhite;
imageWithAlpha.data[pixelIndex + 3] = 255;
} else if (conflictMode === "transparent") {
imageWithAlpha.data[pixelIndex] = 0;
imageWithAlpha.data[pixelIndex + 1] = 0;
imageWithAlpha.data[pixelIndex + 2] = 0;
imageWithAlpha.data[pixelIndex + 3] = 0;
} else {
throw new Error(`unexpected conflict mode ${conflictMode}`);
}
numWarnings++; numWarnings++;
continue; continue;
} }
@ -460,13 +518,29 @@ function mergeDataIntoImageWithAlpha(imageOnBlack, imageOnWhite) {
` vs ` + ` vs ` +
`#${rOnBlack.toString(16)}${bOnBlack.toString(16)}` + `#${rOnBlack.toString(16)}${bOnBlack.toString(16)}` +
`${gOnWhite.toString(16)}. ` + `${gOnWhite.toString(16)}. ` +
`Falling back to the pixel on black, with alpha = 100%. ` `Using conflict mode ${conflictMode} to fall back.`
); );
} }
if (conflictMode === "onBlack") {
imageWithAlpha.data[pixelIndex] = rOnBlack; imageWithAlpha.data[pixelIndex] = rOnBlack;
imageWithAlpha.data[pixelIndex + 1] = gOnBlack; imageWithAlpha.data[pixelIndex + 1] = gOnBlack;
imageWithAlpha.data[pixelIndex + 2] = bOnBlack; imageWithAlpha.data[pixelIndex + 2] = bOnBlack;
imageWithAlpha.data[pixelIndex + 3] = 255; imageWithAlpha.data[pixelIndex + 3] = 255;
} else if (conflictMode === "onWhite") {
imageWithAlpha.data[pixelIndex] = rOnWhite;
imageWithAlpha.data[pixelIndex + 1] = gOnWhite;
imageWithAlpha.data[pixelIndex + 2] = bOnWhite;
imageWithAlpha.data[pixelIndex + 3] = 255;
} else if (conflictMode === "transparent") {
imageWithAlpha.data[pixelIndex] = 0;
imageWithAlpha.data[pixelIndex + 1] = 0;
imageWithAlpha.data[pixelIndex + 2] = 0;
imageWithAlpha.data[pixelIndex + 3] = 0;
} else {
throw new Error(`unexpected conflict mode ${conflictMode}`);
}
numWarnings++; numWarnings++;
continue; continue;
} }