Files
2025-07-18 16:20:14 +07:00

325 lines
11 KiB
PHP
Executable File

<?php
include "../../../include/boot.php";
include "../../../include/authenticate.php";
if (!in_array("openai_gpt" ,$plugins) || !in_array("openai_image_edit", $plugins))
{
exit("The OpenAI GPT and OpenAI Image Editing plugins must be enabled and configured.");
}
// Find image
$ref=getval("ref",0,true);
$access=get_resource_access($ref);
$edit_access=get_edit_access($ref);
if ($access!=0)
{
// They shouldn't arrive here as the link wouldn't be available.
exit("Access denied");
}
function curlprogress($resource,$download_size, $downloaded, $upload_size, $uploaded)
{
// Give an estimate of completion based on the % of upload. There is also the DALL-E 2 processing time but the bulk of the time seems to be the upload due to using PNG for images.
global $lang;
if ($uploaded>0)
{
$percent=floor(($uploaded/$upload_size)*100);
$percent*=7;$percent+=10; // It lags behind a lot, after experimentation, this gives a more reasonable estimate of completion time.
if ($percent>=100)
{
set_processing_message($lang["openai_image_edit__completing"]);
}
else
{
set_processing_message($lang["openai_image_edit__sending"] . " (" . $percent . "%)");
}
}
}
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
set_processing_message($lang["openai_image_edit__preparing_images"]);
$maskData = getval('mask',''); // Base64 encoded mask from the frontend
$mode = getval('mode','');
$prompt = getval('prompt','');
// Decode the mask data from base64
list($type, $maskData) = explode(';', $maskData);
list(, $maskData) = explode(',', $maskData);
$maskData = base64_decode($maskData);
if ($mode=="white" || $mode=="black")
{
$mask=imagecreatefromstring($maskData);
// Get the width and height of the image
$width = imagesx($mask);
$height = imagesy($mask);
// Create a new true color image with the same dimensions
$newBackground = imagecreatetruecolor($width, $height);
// Fill the new image with colour
$shade=255;
if ($mode=="black") {$shade=0;}
$fill = imagecolorallocate($newBackground, $shade, $shade, $shade);
imagefill($newBackground, 0, 0, $fill);
// Copy the original image onto the white background
// This will replace transparent areas with white
imagecopy($newBackground, $mask, 0, 0, 0, 0, $width, $height);
// Start output buffering to capture the image data in memory
ob_start();
imagepng($newBackground);
$imagedata = ob_get_clean();
// Free up memory
imagedestroy($mask);
imagedestroy($newBackground);
// Return the image data as JSON with base64 encoding
header('Content-Type: application/json');
echo json_encode(["image_base64" => base64_encode($imagedata)]);
exit();
}
if ($mode=="clone")
{
$mask=imagecreatefromstring($maskData);
// Get the width and height of the image
$width = imagesx($mask);
$height = imagesy($mask);
// Create a new true color image with the same dimensions
$image = imagecreatefromstring($maskData);
imagealphablending($image, true);
imagesavealpha($image, true);
// Clone parts of the surrounding image by moving it in an ever decreasing box
for ($offset=300;$offset>0;$offset-=30)
{
imagecopy($image, $mask, -$offset, 0, 0, 0, $width, $height);
imagecopy($image, $mask, 0, -$offset, 0, 0, $width, $height);
imagecopy($image, $mask, $offset, 0, 0, 0, $width, $height);
imagecopy($image, $mask, 0, $offset, 0, 0, $width, $height);
}
imagecopy($image, $mask, 0, 0, 0, 0, $width, $height);
// Start output buffering to capture the image data in memory
ob_start();
imagepng($image);
$imagedata = ob_get_clean();
// Free up memory
imagedestroy($mask);
imagedestroy($image);
// Return the image data as JSON with base64 encoding
header('Content-Type: application/json');
echo json_encode(["image_base64" => base64_encode($imagedata)]);
exit();
}
// Prepare the OpenAI API request using multipart/form-data
if ($mode=="edit")
{
$url = 'https://api.openai.com/v1/images/edits';
$model = "dall-e-2";
$content_type="multipart/form-data";
}
if ($mode=="variation")
{
$url = 'https://api.openai.com/v1/images/variations';
$model = "dall-e-2";
$content_type="multipart/form-data";
}
if ($mode=="generate")
{
$url = 'https://api.openai.com/v1/images/generations';
$model = "dall-e-3";
$content_type="application/json";
}
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_PROGRESSFUNCTION, 'curlprogress');
curl_setopt($ch, CURLOPT_NOPROGRESS, false);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
"Authorization: Bearer $openai_gpt_api_key",
"Content-Type: " . $content_type
]);
// Improve the mask by replacing non transparent areas with black. This significantly reduces the time to send the mask to OpenAI as the compression is much better.
$mask=imagecreatefromstring($maskData);
// Set blending mode off to preserve transparency
imagealphablending($mask, false);
imagesavealpha($mask, true);
// Get image dimensions
$width = imagesx($mask);
$height = imagesy($mask);
// Loop through each pixel
for ($x = 0; $x < $width; $x++) {
for ($y = 0; $y < $height; $y++) {
// Get the color and alpha of the current pixel
$rgba = imagecolorat($mask, $x, $y);
$alpha = ($rgba & 0x7F000000) >> 24;
// Set the pixel to black with the same alpha
$black = imagecolorallocatealpha($mask, 0, 0, 0, $alpha);
imagesetpixel($mask, $x, $y, $black);
}
}
// Re-render the mask
ob_start();
imagepng($mask);
$maskDataSimplified = ob_get_contents();
ob_end_clean();
// Prepare the data array using CURLFile for both image and mask
$data = [
'model' => $model, // Specify model (if applicable)
'n' => 1,
'size' => '1024x1024'
];
if ($mode=="edit" || $mode=="variation")
{
$data['image'] = new CURLStringFile($maskData, 'image/png');
}
if ($mode=="edit" || $mode=="generate")
{
$data['prompt'] = $prompt;
}
if ($mode=="edit")
{
$data['mask'] = new CURLStringFile($maskDataSimplified, 'image/png');
}
if ($mode=="generate")
{
$data=json_encode($data);
}
// Attach the form data
curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
// Execute the request and get the response
$response = curl_exec($ch);
// Check for errors in the cURL request
if (curl_errno($ch)) {
echo 'Error:' . curl_error($ch);
} else {
$json=json_decode($response,true);
$url=$json["data"][0]["url"] ?? "";
if ($url!="")
{
header('Content-Type: application/json');
echo json_encode(["image_base64"=>base64_encode(file_get_contents($url))]);
}
else
{
echo $response;
}
}
curl_close($ch);
exit();
}
include "../../../include/header.php";
?>
<div class="BasicsBox">
<?php
renderBreadcrumbs([
[
"title" => $lang["backtoview"],
"href" => generateURL($baseurl_short . "pages/view.php", ["ref" => $ref])
],
[
"title" => $lang["openai_image_edit__edit_with_ai"],
"help" => "plugins/openai_image_edit"
]
]);
?>
</div>
<img id="image" src="get_png.php?ref=<?php echo (int) $ref ?>" alt="" hidden>
<div id="canvas-container" class="canvas-container" style="position: relative;visibility:hidden;">
<canvas id="canvas"></canvas>
<canvas id="overlayCanvas" style="position: absolute; top: 0; left: 0; pointer-events: none;"></canvas>
</div>
<div id="toolbox" class="toolbox openai-image-edit" style="visibility:hidden;">
<div id="tools">
<label for="editMode"><?php echo escape($lang["openai_image_edit__mode"]) ?></label><br>
<select id="editMode">
<option value="edit"><?php echo escape($lang["openai_image_edit__mode_edit"]); ?></option>
<option value="variation"><?php echo escape($lang["openai_image_edit__mode_variation"]); ?></option>
<option value="generate"><?php echo escape($lang["openai_image_edit__mode_generate"]); ?></option>
<option value="white"><?php echo escape($lang["openai_image_edit__mode_white"]); ?></option>
<option value="black"><?php echo escape($lang["openai_image_edit__mode_black"]); ?></option>
<option value="clone"><?php echo escape($lang["openai_image_edit__mode_clone"]); ?></option>
</select>
<br><br>
<label for="penSize"><?php echo escape($lang["openai_image_edit__pensize"]); ?></label><br>
<input type="range" id="penSize" min="10" max="200" value="75">
<br><br>
<label for="prompt"><?php echo escape($lang["openai_image_edit__prompt"]); ?></label><br>
<textarea id="prompt" rows="5" required placeholder="Prompt for regeneration">Complete image as appropriate</textarea>
<br>
<button id="clearBtn" onclick="window.location.reload();"><?php echo escape($lang["openai_image_edit__reset"]); ?></button>
<button id="submitBtn"><?php echo escape($lang["openai_image_edit__generate"]); ?></button>
<br><br><br>
<div id="downloadOptions" style="visibility: hidden;">
<label for="downloadType"><?php echo escape($lang["openai_image_edit__exportoptions"]); ?></label><br>
<select id="downloadType">
<option value="image/jpeg">JPEG</option>
<option value="image/png">PNG</option>
<option value="image/webp">WEBP</option>
</select>
<br>
<select id="downloadAction">
<option value="download"><?php echo escape($lang["openai_image_edit__download"]); ?></option>
<?php if ($edit_access) { ?><option value="alternative"><?php echo escape($lang["openai_image_edit__alternative"]); ?></option><?php } ?>
<?php if (checkperm("c")) { ?><option value="new"><?php echo escape($lang["openai_image_edit__new"]); ?></option><?php } ?>
</select>
<br>
<button id="downloadBtn"><?php echo escape($lang["openai_image_edit__export"]); ?></button>
</div>
</div>
</div>
<script>
CentralSpaceShowProcessing();
submit_url='<?php echo generateURL($baseurl . "/plugins/openai_image_edit/pages/edit.php",[],["ref"=>$ref]); ?>';
alternative_url='<?php echo generateURL($baseurl . "/plugins/openai_image_edit/pages/save_alternative.php",[],["ref"=>$ref]); ?>';
save_new_url='<?php echo generateURL($baseurl . "/plugins/openai_image_edit/pages/save_new.php",[] ,["ref"=>$ref]); ?>';
view_url='<?php echo generateURL($baseurl . "/pages/view.php",[],["ref"=>$ref]); ?>';
view_new_url='<?php echo $baseurl ?>/pages/view.php?ref=';
csrf_pair={<?php echo generateAjaxToken("openai_image_edit"); ?>};
defaultLoadingMessage=<?php echo json_encode($lang["openai_image_edit__preparing_images"]) ?>;
</script>
<script src="../js/edit.js"></script>
<?php
include "../../../include/footer.php";