PNG to DDS Converter
Upload a PNG image to convert it into a basic, uncompressed DDS (DirectDraw Surface) format. This tool creates an RGBA8888 uncompressed DDS file.
Image preview will appear here.
Success! Your PNG has been converted to "${outputFileName}".
Note: This is an uncompressed RGBA8888 DDS file.
`; downloadPdfButton.style.display = 'block'; // Store data for PDF report resultArea.dataset.inputFileName = originalFileName; resultArea.dataset.outputFileName = outputFileName; resultArea.dataset.imageWidth = img.width; resultArea.dataset.imageHeight = img.height; resultArea.dataset.hasAlpha = hasAlphaChannel ? 'Yes' : 'No'; }; img.onerror = function() { resultArea.style.display = 'block'; resultArea.innerHTML = 'Error processing image. Make sure it\'s a valid PNG.
'; downloadPdfButton.style.display = 'none'; }; img.src = e.target.result; }; reader.onerror = function() { resultArea.style.display = 'block'; resultArea.innerHTML = 'Error reading file data.
'; downloadPdfButton.style.display = 'none'; }; reader.readAsDataURL(file); } /** * Builds the DDS header as a Uint8Array. * @param {number} width Image width. * @param {number} height Image height. * @param {boolean} hasAlpha True if the image has an alpha channel. * @returns {Uint8Array} The DDS header bytes. */ function buildDDSHeader(width, height, hasAlpha) { const buffer = new ArrayBuffer(DDS_HEADER_SIZE); const view = new DataView(buffer); // dwSize (124 bytes) view.setUint32(0, DDS_HEADER_SIZE, true); // dwFlags let dwFlags = DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH | DDSD_PIXELFORMAT; dwFlags |= DDSD_PITCH; // Using pitch for uncompressed view.setUint32(4, dwFlags, true); // dwHeight view.setUint32(8, height, true); // dwWidth view.setUint32(12, width, true); // dwPitchOrLinearSize (for uncompressed RGBA, it's width * bytes per pixel (4)) view.setUint32(16, width * 4, true); // dwDepth (0 for 2D textures) view.setUint32(20, 0, true); // dwMipMapCount (0 if no mipmaps) view.setUint32(24, 0, true); // dwReserved1 (11 DWORDS, skip 11 * 4 bytes) - set to 0 by ArrayBuffer default // Pixel Format (ddspf) - 32 bytes from offset 72 const pixelFormatOffset = 72; // ddspf.dwSize (32 bytes) view.setUint32(pixelFormatOffset, 32, true); // ddspf.dwFlags let ddspfFlags = DDPF_RGB; if (hasAlpha) { ddspfFlags |= DDPF_ALPHAPIXELS; } view.setUint32(pixelFormatOffset + 4, ddspfFlags, true); // ddspf.dwFourCC (0 for uncompressed RGB/RGBA) view.setUint32(pixelFormatOffset + 8, 0, true); // ddspf.dwRGBBitCount (32 for RGBA8888) view.setUint32(pixelFormatOffset + 12, 32, true); // ddspf.dwRBitMask, dwGBitMask, dwBBitMask, dwABitMask (Standard RGBA8888 masks) view.setUint32(pixelFormatOffset + 16, 0x000000FF, true); // R (lowest byte) view.setUint32(pixelFormatOffset + 20, 0x0000FF00, true); // G view.setUint32(pixelFormatOffset + 24, 0x00FF0000, true); // B view.setUint32(pixelFormatOffset + 28, 0xFF000000, true); // A (highest byte) // dwCaps (4 DWORDS) - from offset 108 view.setUint32(108, DDSCAPS_TEXTURE, true); // dwCaps1 (just texture) view.setUint32(112, 0, true); // dwCaps2 view.setUint32(116, 0, true); // dwCaps3 view.setUint32(120, 0, true); // dwCaps4 return new Uint8Array(buffer); } /** * Clears all inputs, previews, and results. */ function clearAll() { document.getElementById('pngFileInput').value = ''; // Clear selected file document.getElementById('imagePreview').innerHTML = 'Image preview will appear here.
'; clearResultArea(); } /** * Helper function to clear and hide the result area and PDF button. */ function clearResultArea() { const resultArea = document.getElementById('resultArea'); const downloadPdfButton = document.getElementById('downloadPdfButton'); if (resultArea) { resultArea.style.display = 'none'; resultArea.innerHTML = ''; // Clear stored data for PDF delete resultArea.dataset.inputFileName; delete resultArea.dataset.outputFileName; delete resultArea.dataset.imageWidth; delete resultArea.dataset.imageHeight; delete resultArea.dataset.hasAlpha; } if (downloadPdfButton) { downloadPdfButton.style.display = 'none'; } } /** * Generates a PDF summary of the conversion. */ function generatePdf() { // Check if jsPDF library is loaded if (typeof window.jspdf === 'undefined' || typeof window.jspdf.jsPDF === 'undefined') { console.error('jsPDF library not loaded. Cannot generate PDF.'); const resultArea = document.getElementById('resultArea'); if (resultArea) { resultArea.innerHTML = 'PDF generation failed. Library not loaded.
'; } return; } const { jsPDF } = window.jspdf; const doc = new jsPDF(); const resultArea = document.getElementById('resultArea'); // Retrieve stored data for PDF const inputFileName = resultArea.dataset.inputFileName || 'N/A'; const outputFileName = resultArea.dataset.outputFileName || 'N/A'; const width = resultArea.dataset.imageWidth || 'N/A'; const height = resultArea.dataset.imageHeight || 'N/A'; const alphaChannel = resultArea.dataset.hasAlpha || 'N/A'; const generationDate = new Date().toLocaleString(); let yOffset = 20; // Title doc.setFontSize(24); doc.setTextColor(44, 62, 80); doc.text("PNG to DDS Conversion Summary", 105, yOffset, { align: 'center' }); yOffset += 20; // Summary details doc.setFontSize(14); doc.setTextColor(51, 51, 51); doc.text(`Conversion Date: ${generationDate}`, 20, yOffset); yOffset += 10; doc.text(`Input File: ${inputFileName}`, 20, yOffset); yOffset += 10; doc.text(`Output File: ${outputFileName}`, 20, yOffset); yOffset += 10; doc.text(`Image Dimensions: ${width} x ${height} pixels`, 20, yOffset); yOffset += 10; doc.text(`Alpha Channel Detected: ${alphaChannel}`, 20, yOffset); yOffset += 20; // Conversion Notes doc.setFontSize(12); doc.setTextColor(80, 80, 80); doc.text("Important Notes on DDS Conversion:", 20, yOffset); yOffset += 7; const notes = [ "• This tool generates an **uncompressed RGBA8888 DDS** file.", "• Uncompressed DDS files are significantly larger than compressed DDS files (e.g., DXT1, DXT5).", "• Advanced DDS features like mipmaps or specific compression formats are NOT supported by this tool.", "• For compressed DDS or more complex requirements, dedicated image editing software (e.g., GIMP, Photoshop with DDS plugin) or specialized tools are recommended." ]; notes.forEach(note => { const splitNote = doc.splitTextToSize(note, 170); doc.text(splitNote, 25, yOffset); yOffset += (splitNote.length * 7) + 5; }); yOffset += 15; // Footer doc.setFontSize(9); doc.setTextColor(150, 150, 150); doc.text(`Report Generated by PNG to DDS Converter Tool`, 20, doc.internal.pageSize.height - 15); doc.save("PNG_to_DDS_Conversion_Summary.pdf"); }